From 945899c3da6f69ec862eefd3215bc3b8d1265020 Mon Sep 17 00:00:00 2001 From: Ben DeLay Date: Wed, 26 Mar 2025 10:20:30 -0700 Subject: [PATCH 1/4] C#: Adds version 8 and expands nullability to reference types (#1632) --- .../quicktype-core/src/language/CSharp/CSharpRenderer.ts | 2 +- packages/quicktype-core/src/language/CSharp/language.ts | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/packages/quicktype-core/src/language/CSharp/CSharpRenderer.ts b/packages/quicktype-core/src/language/CSharp/CSharpRenderer.ts index 09cfcc3caf..d07dea90f8 100644 --- a/packages/quicktype-core/src/language/CSharp/CSharpRenderer.ts +++ b/packages/quicktype-core/src/language/CSharp/CSharpRenderer.ts @@ -125,7 +125,7 @@ export class CSharpRenderer extends ConvenienceRenderer { protected nullableCSType(t: Type, follow: (t: Type) => Type = followTargetType, withIssues = false): Sourcelike { t = followTargetType(t); const csType = this.csType(t, follow, withIssues); - if (isValueType(t)) { + if (isValueType(t) || this._csOptions.version >= 8) { return [csType, "?"]; } else { return csType; diff --git a/packages/quicktype-core/src/language/CSharp/language.ts b/packages/quicktype-core/src/language/CSharp/language.ts index e2b4605824..123147d4c5 100644 --- a/packages/quicktype-core/src/language/CSharp/language.ts +++ b/packages/quicktype-core/src/language/CSharp/language.ts @@ -16,7 +16,7 @@ export enum Framework { SystemTextJson = "SystemTextJson" } -export type Version = 5 | 6; +export type Version = 5 | 6 | 8; export interface OutputFeatures { attributes: boolean; helpers: boolean; @@ -55,7 +55,8 @@ export const cSharpOptions = { "C# version", [ ["5", 5], - ["6", 6] + ["6", 6], + ["8", 8] ], "6", "secondary" From 7fb43586d90a450bc454554bc8db7978552d20df Mon Sep 17 00:00:00 2001 From: Ben DeLay Date: Sun, 15 Jun 2025 19:46:57 -0700 Subject: [PATCH 2/4] C#: Adds JsonRequired attribute to System.Text.Json classes for required fields --- .../src/language/CSharp/SystemTextJsonCSharpRenderer.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/packages/quicktype-core/src/language/CSharp/SystemTextJsonCSharpRenderer.ts b/packages/quicktype-core/src/language/CSharp/SystemTextJsonCSharpRenderer.ts index 77afed6da4..dfede9be4e 100644 --- a/packages/quicktype-core/src/language/CSharp/SystemTextJsonCSharpRenderer.ts +++ b/packages/quicktype-core/src/language/CSharp/SystemTextJsonCSharpRenderer.ts @@ -217,10 +217,13 @@ export class SystemTextJsonCSharpRenderer extends CSharpRenderer { const isNullable = followTargetType(property.type).isNullable; const isOptional = property.isOptional; - if (isOptional && !isNullable) { + if (!isOptional) { + attributes.push(["[", "JsonRequired", "]"]); + } else if (isOptional && !isNullable) { attributes.push(["[", "JsonIgnore", "(Condition = JsonIgnoreCondition.WhenWritingNull)]"]); } + // const requiredClass = this._options.dense ? "R" : "Required"; // const nullValueHandlingClass = this._options.dense ? "N" : "NullValueHandling"; // const nullValueHandling = isOptional && !isNullable ? [", NullValueHandling = ", nullValueHandlingClass, ".Ignore"] : []; From 5cd5112bbc41e6ea28085ebf77a8db52dd177053 Mon Sep 17 00:00:00 2001 From: Mark Probst Date: Sun, 19 Jul 2026 18:31:32 -0400 Subject: [PATCH 3/4] C#: fix up version-8 nullable-reference-types support Fixes on top of PR #2694: - Gate the System.Text.Json [JsonRequired] attribute behind the existing check-required option instead of emitting it unconditionally. It now matches the spirit of Newtonsoft's [JsonProperty(Required = ...)]: only emitted for non-optional properties when check-required is on. - Un-skip required.schema, strict-optional.schema and intersection.schema for the System.Text.Json fixture: with [JsonRequired] the renderer now implements check-required, so the expected-failure samples fail as expected. - Emit "#nullable enable" (plus the same CS8618/CS8601/CS8603 pragma suppressions the System.Text.Json renderer already uses) in the Newtonsoft renderer when csharp-version >= 8, so the new "?" reference type annotations compile without warnings. Versions 5 and 6 are unchanged. - Bump the C# test fixtures to net8.0 (and CI to the .NET 8 SDK): [JsonRequired] requires System.Text.Json 7+. - Add { "csharp-version": "8" } quick-test combinations for both the Newtonsoft and System.Text.Json fixtures. Co-Authored-By: Claude Fable 5 --- .github/workflows/test-pr.yaml | 2 +- .../language/CSharp/NewtonSoftCSharpRenderer.ts | 15 +++++++++++++++ .../CSharp/SystemTextJsonCSharpRenderer.ts | 11 ++++++++--- test/fixtures/csharp-SystemTextJson/test.csproj | 4 ++-- test/fixtures/csharp/test.csproj | 2 +- test/languages.ts | 5 ++--- 6 files changed, 29 insertions(+), 10 deletions(-) diff --git a/.github/workflows/test-pr.yaml b/.github/workflows/test-pr.yaml index 6f67d78118..de709a7234 100644 --- a/.github/workflows/test-pr.yaml +++ b/.github/workflows/test-pr.yaml @@ -106,7 +106,7 @@ jobs: if: ${{ contains(matrix.fixture, 'csharp') }} uses: actions/setup-dotnet@v3 with: - dotnet-version: 6 + dotnet-version: 8 - name: Install Rust if: ${{ contains(matrix.fixture, 'rust') }} diff --git a/packages/quicktype-core/src/language/CSharp/NewtonSoftCSharpRenderer.ts b/packages/quicktype-core/src/language/CSharp/NewtonSoftCSharpRenderer.ts index 3c3d91cc9c..207dda662c 100644 --- a/packages/quicktype-core/src/language/CSharp/NewtonSoftCSharpRenderer.ts +++ b/packages/quicktype-core/src/language/CSharp/NewtonSoftCSharpRenderer.ts @@ -190,6 +190,14 @@ export class NewtonsoftCSharpRenderer extends CSharpRenderer { return this._options.baseclass; } + protected emitDefaultFollowingComments(): void { + if (!this._needHelpers || this._options.version < 8) return; + + this.emitLine("#pragma warning restore CS8618"); + this.emitLine("#pragma warning restore CS8601"); + this.emitLine("#pragma warning restore CS8603"); + } + protected emitDefaultLeadingComments(): void { if (!this._needHelpers) return; @@ -223,6 +231,13 @@ export class NewtonsoftCSharpRenderer extends CSharpRenderer { ";", ); }); + + if (this._options.version >= 8) { + this.emitLine("#nullable enable"); + this.emitLine("#pragma warning disable CS8618"); + this.emitLine("#pragma warning disable CS8601"); + this.emitLine("#pragma warning disable CS8603"); + } } private converterForType(t: Type): Name | undefined { diff --git a/packages/quicktype-core/src/language/CSharp/SystemTextJsonCSharpRenderer.ts b/packages/quicktype-core/src/language/CSharp/SystemTextJsonCSharpRenderer.ts index 08a4de9e8b..4acfb7e6e7 100644 --- a/packages/quicktype-core/src/language/CSharp/SystemTextJsonCSharpRenderer.ts +++ b/packages/quicktype-core/src/language/CSharp/SystemTextJsonCSharpRenderer.ts @@ -273,9 +273,14 @@ export class SystemTextJsonCSharpRenderer extends CSharpRenderer { const isNullable = followTargetType(property.type).isNullable; const isOptional = property.isOptional; - if (!isOptional) { - attributes.push(["[", "JsonRequired", "]"]); - } else if (isOptional && !isNullable) { + // [JsonRequired] makes deserialization fail if the property is + // missing from the JSON. It requires System.Text.Json 7.0 or + // later (.NET 7+). + if (this._options.checkRequired && !isOptional) { + attributes.push(["[JsonRequired]"]); + } + + if (isOptional && !isNullable) { attributes.push([ "[", "JsonIgnore", diff --git a/test/fixtures/csharp-SystemTextJson/test.csproj b/test/fixtures/csharp-SystemTextJson/test.csproj index 1f5224ffd3..37d7e4c935 100755 --- a/test/fixtures/csharp-SystemTextJson/test.csproj +++ b/test/fixtures/csharp-SystemTextJson/test.csproj @@ -1,9 +1,9 @@  Exe - net6 + net8.0 - + diff --git a/test/fixtures/csharp/test.csproj b/test/fixtures/csharp/test.csproj index 2fbf1477c5..9b3c4614b1 100755 --- a/test/fixtures/csharp/test.csproj +++ b/test/fixtures/csharp/test.csproj @@ -1,7 +1,7 @@  Exe - netcoreapp6.0 + net8.0 diff --git a/test/languages.ts b/test/languages.ts index 638c3a91d2..4ad55268cc 100644 --- a/test/languages.ts +++ b/test/languages.ts @@ -118,6 +118,7 @@ export const CSharpLanguage: Language = { quickTestRendererOptions: [ { "array-type": "list" }, { "csharp-version": "5" }, + { "csharp-version": "8" }, { density: "dense" }, { "number-type": "decimal" }, { "any-type": "dynamic" }, @@ -160,14 +161,12 @@ export const CSharpLanguageSystemTextJson: Language = { "minmaxlength.schema", // generated converter triggers CS8602 warnings, which "dotnet run" prints to stdout, breaking the JSON comparison "optional-constraints.schema", // same CS8602 stdout issue; also min/max on integers and pattern on optional strings aren't checked, so expected-failure samples don't fail "optional-const-ref.schema", // same CS8602 stdout issue; also min/max on integers isn't checked, so the expected-failure sample doesn't fail - "required.schema", // the renderer doesn't implement check-required, so the expected-failure sample doesn't fail - "strict-optional.schema", // the renderer doesn't implement check-required, so the expected-failure sample doesn't fail - "intersection.schema", // the renderer doesn't implement check-required, so the expected-failure sample doesn't fail ], rendererOptions: { "check-required": "true", framework: "SystemTextJson" }, quickTestRendererOptions: [ { "array-type": "list" }, { "csharp-version": "6" }, + { "csharp-version": "8" }, { density: "dense" }, { "number-type": "decimal" }, { "any-type": "dynamic" }, From b3cb535a4b90d6a1ad5c19a764690427d77fc1f0 Mon Sep 17 00:00:00 2001 From: Mark Probst Date: Sun, 19 Jul 2026 18:41:46 -0400 Subject: [PATCH 4/4] C#: also suppress CS8765 for Newtonsoft at csharp-version 8 The generated Newtonsoft JsonConverter overrides declare their parameters as non-nullable "object" while Newtonsoft.Json 13 annotates them as "object?", which produces CS8765 warnings under "#nullable enable". "dotnet run" prints warnings to stdout, which would break the fixture round-trips. Verified with the .NET 8 SDK: with this suppression every priority/samples JSON input compiles with zero warnings at csharp-version 8 for both the Newtonsoft and System.Text.Json renderers. Co-Authored-By: Claude Fable 5 --- .../src/language/CSharp/NewtonSoftCSharpRenderer.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/quicktype-core/src/language/CSharp/NewtonSoftCSharpRenderer.ts b/packages/quicktype-core/src/language/CSharp/NewtonSoftCSharpRenderer.ts index b4e325988f..1993f6e135 100644 --- a/packages/quicktype-core/src/language/CSharp/NewtonSoftCSharpRenderer.ts +++ b/packages/quicktype-core/src/language/CSharp/NewtonSoftCSharpRenderer.ts @@ -198,6 +198,7 @@ export class NewtonsoftCSharpRenderer extends CSharpRenderer { this.emitLine("#pragma warning restore CS8618"); this.emitLine("#pragma warning restore CS8601"); this.emitLine("#pragma warning restore CS8603"); + this.emitLine("#pragma warning restore CS8765"); } protected emitDefaultLeadingComments(): void { @@ -239,6 +240,7 @@ export class NewtonsoftCSharpRenderer extends CSharpRenderer { this.emitLine("#pragma warning disable CS8618"); this.emitLine("#pragma warning disable CS8601"); this.emitLine("#pragma warning disable CS8603"); + this.emitLine("#pragma warning disable CS8765"); } }