Skip to content

Add JSON Schema → C# records source generator#16

Draft
Copilot wants to merge 3 commits into
mainfrom
copilot/create-library-for-json-schema
Draft

Add JSON Schema → C# records source generator#16
Copilot wants to merge 3 commits into
mainfrom
copilot/create-library-for-json-schema

Conversation

Copy link
Copy Markdown

Copilot AI commented Mar 14, 2026

Introduces a new Roslyn IIncrementalGenerator library that reads .schema.json additional files and emits a #nullable enable C# record hierarchy, with namespacing derived from the file's directory path.

Generator library (src/JsonSchema)

  • JsonSchemaGeneratorIIncrementalGenerator; picks up *.schema.json AdditionalFiles, derives namespace from RootNamespace + subdirectory path, derives type name from file name or schema title
  • SchemaParser – hand-written recursive descent JSON parser; no external dependencies, safe to run in any Roslyn host; handles type as string or array (["string","null"] → nullable)
  • CodeEmitter – emits #nullable enable records with get; init; property accessors; required properties → non-nullable (with = default!; for reference types), optional → nullable; nested object properties → additional sibling records named {Parent}{Prop}; each property is decorated with [JsonPropertyName(...)] and, when available, [Description(...)]

Type mapping

JSON Schema Format C#
string string
string date-time global::System.DateTimeOffset
string date / time DateOnly / TimeOnly
string uuid/guid global::System.Guid
integer — / int64 int / long
number — / float double / float
boolean bool
array T[]
object + additionalProperties Dictionary<string, T>
object + properties nested record

Property attributes

Each generated property is decorated with:

  • [global::System.Text.Json.Serialization.JsonPropertyName("originalName")] — always present, preserves the original JSON key casing for serialisation round-trips
  • [global::System.ComponentModel.Description("...")] — emitted only when the JSON Schema property has a description field

Example

Given Models/Person.schema.json:

{
  "type": "object",
  "required": ["id", "name"],
  "properties": {
    "id":      { "type": "string", "format": "uuid" },
    "name":    { "type": "string", "description": "Full display name" },
    "age":     { "type": "integer" },
    "address": {
      "type": "object",
      "required": ["street"],
      "properties": {
        "street": { "type": "string" },
        "city":   { "type": "string" }
      }
    }
  }
}

Emits:

// <auto-generated/>
#nullable enable

namespace MyApp.Models
{
    public record Person
    {
        [global::System.Text.Json.Serialization.JsonPropertyName("id")]
        public global::System.Guid Id { get; init; }

        [global::System.Text.Json.Serialization.JsonPropertyName("name")]
        [global::System.ComponentModel.Description("Full display name")]
        public string Name { get; init; } = default!;

        [global::System.Text.Json.Serialization.JsonPropertyName("age")]
        public int? Age { get; init; }

        [global::System.Text.Json.Serialization.JsonPropertyName("address")]
        public PersonAddress? Address { get; init; }
    }

    public record PersonAddress
    {
        [global::System.Text.Json.Serialization.JsonPropertyName("street")]
        public string Street { get; init; } = default!;

        [global::System.Text.Json.Serialization.JsonPropertyName("city")]
        public string? City { get; init; }
    }
}

Other changes

  • global.json – updated SDK from 6.0.100 (unavailable) to 8.0.100 with latestMinor rollforward
  • tests/Tests.JsonSchema – 49 unit tests covering parser, emitter, and the Roslyn generator driver
Original prompt

let's create a new library that uses source generators to create a c# class hierarchy based on the path of a file containing a Json schema.

the generator should support NRE, records, use arrays and dictionaries, and use type annotation to select the correct .net primitive type.


🔒 GitHub Advanced Security automatically protects Copilot coding agent pull requests. You can protect all pull requests by enabling Advanced Security for your repositories. Learn more about Advanced Security.

Co-authored-by: Kralizek <2458439+Kralizek@users.noreply.github.com>
Copilot AI changed the title [WIP] Add library for generating class hierarchy from Json schema Add JSON Schema → C# records source generator Mar 14, 2026
Copilot AI requested a review from Kralizek March 14, 2026 18:09
Copy link
Copy Markdown
Contributor

@Kralizek Kralizek left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of records with primary constructors, use classic properties with get; init; accessors. Also decorate them with attributes to match the name and the description if available.

…e and Description attributes

Co-authored-by: Kralizek <2458439+Kralizek@users.noreply.github.com>
Copy link
Copy Markdown
Author

Copilot AI commented Mar 14, 2026

Instead of records with primary constructors, use classic properties with get; init; accessors. Also decorate them with attributes to match the name and the description if available.

Addressed in c9dd4b8 — see the reply above for the full details and a sample of the new generated output.

Copilot AI requested a review from Kralizek March 14, 2026 20:58
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants