From 0757accc35801bfd02a201693d9c6ffb69548c1a Mon Sep 17 00:00:00 2001 From: Mark Probst Date: Mon, 20 Jul 2026 19:17:28 -0400 Subject: [PATCH 1/3] fix(golang): don't apply omitempty to nullable/union fields under --omit-empty (#2515) When a oneOf branch unification makes a nullable/union-typed property optional, --omit-empty tagged it json:"field,omitempty". Since Go's omitempty on a pointer conflates "absent" with an explicit JSON null, this silently dropped required null values (e.g. {"kind":"one","b":null}) from marshalled output, producing schema-invalid JSON. canOmitEmpty already excluded union/null/any-typed properties from omitempty in the default (flag-off) path; --omit-empty unconditionally bypassed that exclusion. Now the exclusion applies regardless of the flag, while --omit-empty still widens omitempty to other optional property types as intended. Added test/inputs/schema/nullable-optional-one-of.schema (a oneOf where one variant requires a nullable property, the other doesn't) run with omit-empty via GoLanguage.quickTestRendererOptions, and updated the existing omit-empty fixture's expected output to reflect that nullable fields now preserve null instead of being dropped. Co-Authored-By: gpt-5.6-sol via pi --- .../src/language/Golang/utils.ts | 3 +- .../priority/omit-empty.out.omit-empty.json | 6 ++- .../schema/nullable-optional-one-of.1.json | 4 ++ .../schema/nullable-optional-one-of.schema | 42 +++++++++++++++++++ test/languages.ts | 5 ++- 5 files changed, 55 insertions(+), 5 deletions(-) create mode 100644 test/inputs/schema/nullable-optional-one-of.1.json create mode 100644 test/inputs/schema/nullable-optional-one-of.schema diff --git a/packages/quicktype-core/src/language/Golang/utils.ts b/packages/quicktype-core/src/language/Golang/utils.ts index 6ba526ed2f..6c202655f1 100644 --- a/packages/quicktype-core/src/language/Golang/utils.ts +++ b/packages/quicktype-core/src/language/Golang/utils.ts @@ -51,7 +51,8 @@ export function canOmitEmpty( omitEmptyOption: boolean, ): boolean { if (!cp.isOptional) return false; - if (omitEmptyOption) return true; + if (omitEmptyOption) + return !["union", "null", "any"].includes(cp.type.kind); const t = cp.type; return !["union", "null", "any"].includes(t.kind); } diff --git a/test/inputs/json/priority/omit-empty.out.omit-empty.json b/test/inputs/json/priority/omit-empty.out.omit-empty.json index 809e3cfa02..87924cedf4 100644 --- a/test/inputs/json/priority/omit-empty.out.omit-empty.json +++ b/test/inputs/json/priority/omit-empty.out.omit-empty.json @@ -1,10 +1,12 @@ { "results": [ { - "age": 52 + "age": 52, + "name": null }, { - "age": 27 + "age": 27, + "name": null } ] } diff --git a/test/inputs/schema/nullable-optional-one-of.1.json b/test/inputs/schema/nullable-optional-one-of.1.json new file mode 100644 index 0000000000..29aeb7de27 --- /dev/null +++ b/test/inputs/schema/nullable-optional-one-of.1.json @@ -0,0 +1,4 @@ +{ + "kind": "one", + "b": null +} diff --git a/test/inputs/schema/nullable-optional-one-of.schema b/test/inputs/schema/nullable-optional-one-of.schema new file mode 100644 index 0000000000..1b0759ac35 --- /dev/null +++ b/test/inputs/schema/nullable-optional-one-of.schema @@ -0,0 +1,42 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "definitions": { + "bar": { + "type": ["string", "null"] + }, + "one": { + "type": "object", + "properties": { + "kind": { + "type": "string", + "const": "one" + }, + "b": { + "$ref": "#/definitions/bar" + } + }, + "required": ["kind", "b"] + }, + "two": { + "type": "object", + "properties": { + "kind": { + "type": "string", + "const": "two" + }, + "b": { + "$ref": "#/definitions/bar" + } + }, + "required": ["kind"] + } + }, + "oneOf": [ + { + "$ref": "#/definitions/one" + }, + { + "$ref": "#/definitions/two" + } + ] +} diff --git a/test/languages.ts b/test/languages.ts index 70fa741ebf..f9bb82b8ab 100644 --- a/test/languages.ts +++ b/test/languages.ts @@ -521,9 +521,10 @@ export const GoLanguage: Language = { rendererOptions: {}, quickTestRendererOptions: [ // Runs against the expected-output file - // `omit-empty.out.omit-empty.json`, which asserts that `omitempty` - // actually drops the null field. + // `omit-empty.out.omit-empty.json`, which asserts that nullable + // fields preserve null instead of being omitted. ["omit-empty.json", { "omit-empty": "true" }], + ["nullable-optional-one-of.schema", { "omit-empty": "true" }], ], sourceFiles: ["src/language/Golang/index.ts"], }; From 90c0961b9d732804dffb2758c536864d460924b0 Mon Sep 17 00:00:00 2001 From: Mark Probst Date: Mon, 20 Jul 2026 19:36:08 -0400 Subject: [PATCH 2/3] test: add missing fixture cases for nullable-optional-one-of.schema (#3057) Co-Authored-By: Claude --- test/inputs/schema/nullable-optional-one-of.1.fail.enum.json | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 test/inputs/schema/nullable-optional-one-of.1.fail.enum.json diff --git a/test/inputs/schema/nullable-optional-one-of.1.fail.enum.json b/test/inputs/schema/nullable-optional-one-of.1.fail.enum.json new file mode 100644 index 0000000000..fd710347dc --- /dev/null +++ b/test/inputs/schema/nullable-optional-one-of.1.fail.enum.json @@ -0,0 +1,4 @@ +{ + "kind": "three", + "b": null +} From 10b1f563cd481515f5a917f19cd9502e15b5933a Mon Sep 17 00:00:00 2001 From: Mark Probst Date: Mon, 20 Jul 2026 21:08:01 -0400 Subject: [PATCH 3/3] fix CI: skip enum-value validation for nullable-optional-one-of.schema on cjson (#2515) The .1.fail.enum.json sample added in #3057 relies on the generated code rejecting an invalid "kind" enum value, but cjson's enum decoder silently falls back to the first variant instead of failing. Add the schema to the existing skipsEnumValueValidation list, which already covers this same gap for cjson/crystal/swift/haskell/typescript-zod/typescript-effect-schema. Co-Authored-By: gpt-5.6-sol via pi --- test/languages.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/test/languages.ts b/test/languages.ts index f9bb82b8ab..896015644a 100644 --- a/test/languages.ts +++ b/test/languages.ts @@ -25,6 +25,7 @@ const skipsEnumValueValidation = [ "enum-large.schema", "optional-enum.schema", "const-non-string.schema", + "nullable-optional-one-of.schema", ]; // The language makes no int/double distinction in unions (e.g. an integer is