RhoMicro.CodeAnalysis.JsonSchemaGenerator 17.1.2

dotnet add package RhoMicro.CodeAnalysis.JsonSchemaGenerator --version 17.1.2                
NuGet\Install-Package RhoMicro.CodeAnalysis.JsonSchemaGenerator -Version 17.1.2                
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="RhoMicro.CodeAnalysis.JsonSchemaGenerator" Version="17.1.2">
  <PrivateAssets>all</PrivateAssets>
  <IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
</PackageReference>                
For projects that support PackageReference, copy this XML node into the project file to reference the package.
paket add RhoMicro.CodeAnalysis.JsonSchemaGenerator --version 17.1.2                
#r "nuget: RhoMicro.CodeAnalysis.JsonSchemaGenerator, 17.1.2"                
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
// Install RhoMicro.CodeAnalysis.JsonSchemaGenerator as a Cake Addin
#addin nuget:?package=RhoMicro.CodeAnalysis.JsonSchemaGenerator&version=17.1.2

// Install RhoMicro.CodeAnalysis.JsonSchemaGenerator as a Cake Tool
#tool nuget:?package=RhoMicro.CodeAnalysis.JsonSchemaGenerator&version=17.1.2                

icon

JsonSchemaGenerator

Generate json schemata from class definitions with ease.

Licensing

This source code generator and the included cli tool are licensed to you under the GPLv3 (see the license for the generator and the license for the cli tool). The code generated by the tool, however, is not subject to these license terms, but rather the license of whatever project it is being used in.

Features

  • generate bespoke json schemata from C# class definitions
  • supported primitive types:
    • primitives: float, double, decimal, sbyte, short, int, long, byte, ushort, uint, ulong, bool, string, object
    • date types: DateTime, TimeSpan, TimeOnly, DateOnly, DateTimeOffset
    • list-like types: T[], ISet<T>, IReadOnlySet<T>, HashSet<T>, List<T>
    • map-like types: IDictionary<TKey, TValue>, IReadOnlyDictionary<TKey, TValue>, Dictionary<TKey, TValue>
  • generate nested json schemata for all other types used in properties

Installation

Package Reference:

<ItemGroup>
	<PackageReference Include="RhoMicro.CodeAnalysis.JsonSchemaGenerator" Version="*">
	<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
	<PrivateAssets>all</PrivateAssets>
	</PackageReference>
</ItemGroup>

CLI:

dotnet add package RhoMicro.CodeAnalysis.JsonSchemaGenerator

The cli tool extracting generated schemata to directories in the output directory requires dotnet 9 to be installed.

How To Use

The generator is instructed via three attributes:

  • RhoMicro.CodeAnalysis.JsonSchemaAttribute
  • RhoMicro.CodeAnalysis.JsonSchemaPropertyAttribute
  • RhoMicro.CodeAnalysis.JsonSchemaExcludeAttribute as well as two MSBuild properties:
  • JsonSchemaGeneratorIntermediateOutputPath
  • JsonSchemaGeneratorOutputPath

Only public get/set properties will be considered when examining schema classes.

Attributes

JsonSchemaAttribute

Annotate classes with this attribute to generate a json schema for it.

JsonSchemaPropertyAttribute

Annotate properties of schema classes with this attribute to provide a title and/or description for that property.

JsonSchemaExcludeAttribute

Annotate properties of schema classes with this attribute to exclude them from schema generation.

MSBuild Properties

JsonSchemaGeneratorIntermediateOutputPath

This is the directory in the intermediate (obj) directory that schemata will be stored at. The default value is JsonSchemata.

JsonSchemaGeneratorOutputPath

This is the directory in the output (bin) directory that schemata from the JsonSchemaGeneratorIntermediateOutputPath of referenced projects will be stored at. The default value is JsonSchemata.

Examples

Simple Example

For this example, a project named HelloWorld is used.

  1. Create a class to generate a schema for:
namespace HelloWorld.MyNamespace;

using RhoMicro.CodeAnalysis;

[JsonSchema]
class Foo1
{
    public Int32 Int32Property { get; set; }
}
  1. Build your project.

  2. Observe the generated schemata in the JsonSchemaGeneratorIntermediateOutputPath and JsonSchemaGeneratorOutputPath:

{
  "properties": {
    "Int32Property": {
      "type": [
        "integer"
      ]
    }
  },
  "$id": "./HelloWorld/HelloWorld/MyNamespace/Foo1.json",
  "type": [
    "object"
  ],
  "additionalProperties": false
}

/HelloWorld/HelloWorld/MyNamespace/Foo1.json

Note how generated schemata will be saved to a directory corresponding to their namespace and assembly name, hence the HelloWorld/HelloWorld/MyNamespace directory structure.

List-Like Types

The following types will cause array schemata to be generated:

  • T[]
  • ISet<T>
  • IReadOnlySet<T>
  • HashSet<T>
  • List<T>

For this example, a project named HelloWorld is used.

  1. Create a class to generate a schema for:
namespace HelloWorld.MyNamespace;

using RhoMicro.CodeAnalysis;

[JsonSchema]
class Foo2
{
    public Int32[] ArrayProperty { get; set; } = [];
    public ISet<Int32> ISetProperty { get; set; } = new HashSet<Int32>();
    public IReadOnlySet<Int32> IReadOnlySetProperty { get; set; } = new HashSet<Int32>();
    public HashSet<Int32> HashSetProperty { get; set; } = [];
    public List<Int32> ListProperty { get; set; } = [];
}
  1. Build your project.

  2. Observe the generated schemata in the JsonSchemaGeneratorIntermediateOutputPath and JsonSchemaGeneratorOutputPath:

{
  "properties": {
    "ArrayProperty": {
      "items": {
        "type": [
          "integer"
        ]
      },
      "type": [
        "array"
      ]
    },
    "ISetProperty": {
      "items": {
        "type": [
          "integer"
        ]
      },
      "type": [
        "array"
      ]
    },
    "IReadOnlySetProperty": {
      "items": {
        "type": [
          "integer"
        ]
      },
      "type": [
        "array"
      ]
    },
    "HashSetProperty": {
      "items": {
        "type": [
          "integer"
        ]
      },
      "type": [
        "array"
      ]
    },
    "ListProperty": {
      "items": {
        "type": [
          "integer"
        ]
      },
      "type": [
        "array"
      ]
    }
  },
  "$id": "./HelloWorld/HelloWorld/MyNamespace/Foo2.json",
  "type": [
    "object"
  ],
  "additionalProperties": false
}

./JsonSchemata/HelloWorld/HelloWorld/MyNamespace/Foo2.json

Map-Like Types

The following types will cause object schemata to be generated:

  • IDictionary<TKey, TValue>
  • IReadOnlyDictionary<TKey, TValue>
  • Dictionary<TKey, TValue>
  • object

For this example, a project named HelloWorld is used.

  1. Create a class to generate a schema for:
namespace HelloWorld.MyNamespace;

using RhoMicro.CodeAnalysis;

[JsonSchema]
class Foo3
{
    public IDictionary<String, Int32> IDictionaryProperty { get; set; } = new Dictionary<String, Int32>();
    public IReadOnlyDictionary<String, Int32> IReadOnlyDictionaryProperty { get; set; } = new Dictionary<String, Int32>();
    public Dictionary<String, Int32> DictionaryProperty { get; set; } = [];
}
  1. Build your project.

  2. Observe the generated schemata in the JsonSchemaGeneratorIntermediateOutputPath and JsonSchemaGeneratorOutputPath:

{
  "properties": {
    "IDictionaryProperty": {
      "type": [
        "object"
      ],
      "additionalProperties": {
        "type": [
          "integer"
        ]
      }
    },
    "IReadOnlyDictionaryProperty": {
      "type": [
        "object"
      ],
      "additionalProperties": {
        "type": [
          "integer"
        ]
      }
    },
    "DictionaryProperty": {
      "type": [
        "object"
      ],
      "additionalProperties": {
        "type": [
          "integer"
        ]
      }
    }
  },
  "$id": "./HelloWorld/HelloWorld/MyNamespace/Foo3.json",
  "type": [
    "object"
  ],
  "additionalProperties": false
}

/HelloWorld/HelloWorld/MyNamespace/Foo3.json

Currently, all Map-Like types are expressed using the object type, irrespective of their TKey argument. This may change in the future to support other key types. The issue is being tracked here: #49

Primitive Types

All C# primitive types are supported:

  • float
  • double
  • decimal
  • sbyte
  • short
  • int
  • long
  • byte
  • ushort
  • uint
  • ulong
  • bool
  • string
  • object

For this example, a project named HelloWorld is used.

  1. Create a class to generate a schema for:
namespace HelloWorld.MyNamespace;

using RhoMicro.CodeAnalysis;

[JsonSchema]
class Foo4
{
    public Single SingleProperty { get; set; }
    public Double DoubleProperty { get; set; }
    public Decimal DecimalProperty { get; set; }
    public SByte SByteProperty { get; set; }
    public Int16 Int16Property { get; set; }
    public Int32 Int32Property { get; set; }
    public Int64 Int64Property { get; set; }
    public Byte ByteProperty { get; set; }
    public UInt16 UInt16Property { get; set; }
    public UInt32 UInt32Property { get; set; }
    public UInt64 UInt64Property { get; set; }
    public Boolean BooleanProperty { get; set; }
    public String StringProperty { get; set; } = String.Empty;
    public Object ObjectProperty { get; set; } = new();
}

  1. Build your project.

  2. Observe the generated schemata in the JsonSchemaGeneratorIntermediateOutputPath and JsonSchemaGeneratorOutputPath:

{
  "properties": {
    "SingleProperty": {
      "type": [
        "number"
      ]
    },
    "DoubleProperty": {
      "type": [
        "number"
      ]
    },
    "DecimalProperty": {
      "type": [
        "number"
      ]
    },
    "SByteProperty": {
      "type": [
        "integer"
      ]
    },
    "Int16Property": {
      "type": [
        "integer"
      ]
    },
    "Int32Property": {
      "type": [
        "integer"
      ]
    },
    "Int64Property": {
      "type": [
        "integer"
      ]
    },
    "ByteProperty": {
      "type": [
        "integer"
      ]
    },
    "UInt16Property": {
      "type": [
        "integer"
      ]
    },
    "UInt32Property": {
      "type": [
        "integer"
      ]
    },
    "UInt64Property": {
      "type": [
        "integer"
      ]
    },
    "BooleanProperty": {
      "type": [
        "boolean"
      ]
    },
    "StringProperty": {
      "type": [
        "string"
      ]
    },
    "ObjectProperty": {
      "type": [
        "object"
      ],
      "additionalProperties": false
    }
  },
  "$id": "./HelloWorld/HelloWorld/MyNamespace/Foo4.json",
  "type": [
    "object"
  ],
  "additionalProperties": false
}

/HelloWorld/HelloWorld/MyNamespace/Foo4.json

Notice that integral types are expressed using the integer type, while floating- and fixed-point numbers are expressed using the number type.

Date Types

The following date types cause string schemata to be generated:

  • DateTime
  • TimeSpan
  • TimeOnly
  • DateOnly
  • DateTimeOffset

For this example, a project named HelloWorld is used.

  1. Create a class to generate a schema for:
namespace HelloWorld.MyNamespace;

using RhoMicro.CodeAnalysis;

[JsonSchema]
class Foo5
{
    public DateTime DateTimeProperty { get; set; }
    public TimeSpan TimeSpanProperty { get; set; }
    public TimeOnly TimeOnlyProperty { get; set; }
    public DateOnly DateOnlyProperty { get; set; }
    public DateTimeOffset DateTimeOffsetProperty { get; set; }
}
  1. Build your project.

  2. Observe the generated schemata in the JsonSchemaGeneratorIntermediateOutputPath and JsonSchemaGeneratorOutputPath:

{
  "properties": {
    "DateTimeProperty": {
      "type": [
        "string"
      ]
    },
    "TimeSpanProperty": {
      "type": [
        "string"
      ]
    },
    "TimeOnlyProperty": {
      "type": [
        "string"
      ]
    },
    "DateOnlyProperty": {
      "type": [
        "string"
      ]
    },
    "DateTimeOffsetProperty": {
      "type": [
        "string"
      ]
    }
  },
  "$id": "./HelloWorld/HelloWorld/MyNamespace/Foo5.json",
  "type": [
    "object"
  ],
  "additionalProperties": false
}

/HelloWorld/HelloWorld/MyNamespace/Foo5.json

Nullable Value Types

Nullable value types cause combined schemata to be generated.

For this example, a project named HelloWorld is used.

  1. Create a class to generate a schema for:
namespace HelloWorld.MyNamespace;

using RhoMicro.CodeAnalysis;

[JsonSchema]
class Foo6
{
    public Int32? NullableInt32Property { get; set; }
}
  1. Build your project.

  2. Observe the generated schemata in the JsonSchemaGeneratorIntermediateOutputPath and JsonSchemaGeneratorOutputPath:

{
  "properties": {
    "NullableInt32Property": {
      "type": [
        "null",
        "integer"
      ]
    }
  },
  "$id": "./HelloWorld/HelloWorld/MyNamespace/Foo6.json",
  "type": [
    "object"
  ],
  "additionalProperties": false
}

/HelloWorld/HelloWorld/MyNamespace/Foo6.json

Nullable Reference Types

Nullable reference types cause combined schemata to be generated.

For this example, a project named HelloWorld is used.

  1. Create a class to generate a schema for:
namespace HelloWorld.MyNamespace;

using RhoMicro.CodeAnalysis;

[JsonSchema]
class Foo7
{
    public Object? NullableObjectProperty { get; set; }
}
  1. Build your project.

  2. Observe the generated schemata in the JsonSchemaGeneratorIntermediateOutputPath and JsonSchemaGeneratorOutputPath:

{
  "properties": {
    "NullableObjectProperty": {
      "type": [
        "null",
        "object"
      ],
      "additionalProperties": false
    }
  },
  "$id": "./HelloWorld/HelloWorld/MyNamespace/Foo7.json",
  "type": [
    "object"
  ],
  "additionalProperties": false
}

/HelloWorld/HelloWorld/MyNamespace/Foo7.json

Required Properties

Properties annotated with the required keyword are included in the required properties list.

For this example, a project named HelloWorld is used.

  1. Create a class to generate a schema for:
[JsonSchema]
class Foo8
{
    public required Object RequiredObjectProperty { get; set; }
}
  1. Build your project.

  2. Observe the generated schemata in the JsonSchemaGeneratorIntermediateOutputPath and JsonSchemaGeneratorOutputPath:

{
  "properties": {
    "RequiredObjectProperty": {
      "type": [
        "object"
      ],
      "additionalProperties": false
    }
  },
  "required": [
    "RequiredObjectProperty"
  ],
  "$id": "./HelloWorld/HelloWorld/MyNamespace/Foo8.json",
  "type": [
    "object"
  ],
  "additionalProperties": false
}

/HelloWorld/HelloWorld/MyNamespace/Foo8.json

Enum Properties

Enum properties cause all enum names and values, as well as their underlying type to be expressed in the genrated schema.

For this example, a project named HelloWorld is used.

  1. Create a class to generate a schema for:
namespace HelloWorld.MyNamespace;

using RhoMicro.CodeAnalysis;

enum MyEnum
{
    None,
    First,
    Second = 42
}

[JsonSchema]
class Foo9
{
    public MyEnum EnumProperty { get; set; }
}
  1. Build your project.

  2. Observe the generated schemata in the JsonSchemaGeneratorIntermediateOutputPath and JsonSchemaGeneratorOutputPath:

{
  "properties": {
    "EnumProperty": {
      "anyOf": [
        {
          "type": [
            "integer"
          ]
        },
        {
          "enum": [
            "None",
            "First",
            "Second",
            0,
            1,
            42
          ]
        }
      ]
    }
  },
  "$id": "./HelloWorld/HelloWorld/MyNamespace/Foo9.json",
  "type": [
    "object"
  ],
  "additionalProperties": false
}

/HelloWorld/HelloWorld/MyNamespace/Foo9.json

Schema References

Referencing another class annotated with the JsonSchemaAttribute will cause a $ref schema to be generated.

For this example, a project named HelloWorld is used.

  1. Create a classes to generate a schema for:
namespace HelloWorld.MyNamespace;

using RhoMicro.CodeAnalysis;

[JsonSchema]
class Foo11
{
    public Foo10 SchemaReferenceProperty { get; set; } = new();
}

[JsonSchema]
class Foo10;
  1. Build your project.

  2. Observe the generated schemata in the JsonSchemaGeneratorIntermediateOutputPath and JsonSchemaGeneratorOutputPath:

{
  "$id": "./HelloWorld/HelloWorld/MyNamespace/Foo10.json",
  "type": [
    "object"
  ],
  "additionalProperties": false
}

/HelloWorld/HelloWorld/MyNamespace/Foo10.json

{
  "properties": {
    "SchemaReferenceProperty": {
      "$ref": "../../../HelloWorld/HelloWorld/MyNamespace/Foo10.json"
    }
  },
  "$id": "./HelloWorld/HelloWorld/MyNamespace/Foo11.json",
  "type": [
    "object"
  ],
  "additionalProperties": false
}

/HelloWorld/HelloWorld/MyNamespace/Foo11.json

POCO References

Referencing another class that is neither a map-like, list-like, primitive, date nor schema (annotated) type will cause a nested schema to be generated for that class.

For this example, a project named HelloWorld is used.

  1. Create a class to generate a schema for:
namespace HelloWorld.MyNamespace;

using RhoMicro.CodeAnalysis;

[JsonSchema]
class Foo13
{
    public Foo12 PocoReferenceProperty { get; set; } = new();
}

class Foo12;
  1. Build your project.

  2. Observe the generated schemata in the JsonSchemaGeneratorIntermediateOutputPath and JsonSchemaGeneratorOutputPath:

{
  "properties": {
    "PocoReferenceProperty": {
      "type": [
        "object"
      ],
      "additionalProperties": false
    }
  },
  "$id": "./HelloWorld/HelloWorld/MyNamespace/Foo13.json",
  "type": [
    "object"
  ],
  "additionalProperties": false
}

/HelloWorld/HelloWorld/MyNamespace/Foo13.json

Warning: Currently, a stack overflow will be caused when defining circular references in POCO schemata:

class Foo12
{
    public Foo12? Foo { get; set; }
}

This issue is being tracked here: #48

Appsettings

This generator may be used to supply the schemata for classes bound against json configuration files via the options pattern. For this setup, a helper schema appsettings.schema.json is used to define which sections of the configuration should be bound against which schema. This helper schema is managed in two variations: one for development and one for release. The development helper schema appsettings.schema.json references the generated schemata relative to the project directory. The release schema appsettings.release.schema.json references the generated schemata relative to the output directory. Upon building the project, the release schema is copied to the output directory under the name appsettings.schema.json.

For this example, a project named HelloWorld is used.

  1. Create a class to generate a schema for:
namespace HelloWorld.MyNamespace;

using RhoMicro.CodeAnalysis;

[JsonSchema]
class Foo14
{
    public Double DoubleProperty { get; set; }
    public required String[] StringArrayProperty { get; set; }
}
  1. Create an appsettings.schema.json file:
{
    "properties": {
        "Foo": {
            "$ref": "./bin/Debug/net9.0/JsonSchemata/HelloWorld/HelloWorld/MyNamespace/Foo14.json"
        }
    }
}
  1. Create an appsettings.release.schema.json file:
{
    "properties": {
        "Foo": {
            "$ref": "./JsonSchemata/HelloWorld/HelloWorld/MyNamespace/Foo14.json"
        }
    }
}
  1. Create an appsettings.json file:
{
  "$schema": "./appsettings.schema.json"
}
  1. Add tasks for copying the appsettings.json and appsettings.release.json files to the output directory:
<ItemGroup>
    <None Update="appsettings.json">
        <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
    </None>
</ItemGroup>

<Target Name="CopyAppsettingsJsonSchema" AfterTargets="Build">
    <Copy SourceFiles="appsettings.schema.release.json" DestinationFiles="$(TargetDir)\appsettings.schema.json" />
</Target>
  1. Build your project.

  2. Observe the generated schemata in the JsonSchemaGeneratorIntermediateOutputPath and JsonSchemaGeneratorOutputPath:

{
  "properties": {
    "DoubleProperty": {
      "type": [
        "number"
      ]
    },
    "StringArrayProperty": {
      "items": {
        "type": [
          "string"
        ]
      },
      "type": [
        "array"
      ]
    }
  },
  "required": [
    "StringArrayProperty"
  ],
  "$id": "./HelloWorld/HelloWorld/MyNamespace/Foo14.json",
  "type": [
    "object"
  ],
  "additionalProperties": false
}

/HelloWorld/HelloWorld/MyNamespace/Foo14.json

  1. Observe intellisense being available for the appsettings.json file:

intellisense

  1. Observe schema validation being available for the appsettings.json file:

validation

Once the project is built, the generated schema will be referenced by the appsettings.schema.json schema and available in the appsettings.json file in the project directory. Also, the appsettings.release.schema.json file will have been copied to the output directory as appsettings.schema.json and reference the schemata as well, making them available to the appsettings.json file in that directory.

Reference Schemata From Other Projects

References to schema classes from other projects will cause $ref schemata to be generated.

For this example, two projects named HelloWorld and Library are used, where the HelloWorld project is referencing the Library project.

  1. Create a consuming class to generate a schema for:
namespace HelloWorld.MyNamespace;

using RhoMicro.CodeAnalysis;

[JsonSchema]
class Foo15
{
    public required Library.LibraryNamespace.Foo FooProperty { get; set; } 
}
  1. Create a class to be consumed and generate a schema for:
namespace Library.LibraryNamespace;

using RhoMicro.CodeAnalysis;

[JsonSchema]
public class Foo
{
    public Int32 Int32Property { get; set; }
}
  1. Build the project.

  2. Observe the generated schemata in the JsonSchemaGeneratorIntermediateOutputPath and JsonSchemaGeneratorOutputPath :

{
  "properties": {
    "FooProperty": {
      "$ref": "../../../Library/Library/LibraryNamespace/Foo.json"
    }
  },
  "required": [
    "FooProperty"
  ],
  "$id": "./HelloWorld/HelloWorld/MyNamespace/Foo15.json",
  "type": [
    "object"
  ],
  "additionalProperties": false
}

/HelloWorld/HelloWorld/MyNamespace/Foo15.json

{
  "properties": {
    "Int32Property": {
      "type": [
        "integer"
      ]
    }
  },
  "$id": "./Library/Library/LibraryNamespace/Foo.json",
  "type": [
    "object"
  ],
  "additionalProperties": false
}

/Library/Library/LibraryNamespace/Foo.json

Contributions

Thanks to wasabii for his help with the msbuild integration.

There are no supported framework assets in this package.

Learn more about Target Frameworks and .NET Standard.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last updated
17.1.2 71 12/30/2024
17.1.1 73 12/30/2024
17.1.0 73 12/30/2024
17.0.2 82 12/24/2024
16.1.2 72 12/22/2024
16.1.1 72 12/21/2024
16.1.0 77 12/20/2024
16.0.4 78 12/18/2024
16.0.3 78 12/18/2024
16.0.0 76 12/18/2024
15.3.4 84 12/7/2024
15.3.3 85 12/7/2024
15.3.2 87 12/7/2024
15.3.1 86 12/7/2024
15.3.0 84 12/6/2024
15.2.4 85 12/6/2024
15.2.3 94 12/6/2024