From 33101e87317dea68295974a74889827d92feae26 Mon Sep 17 00:00:00 2001 From: Marcin Romaszewicz Date: Fri, 13 Feb 2026 20:39:51 -0800 Subject: [PATCH] fix: add omitempty to optional nullable fields Fixes #2091 The `omitempty` JSON tag was not being added to optional nullable fields. The condition `!p.Nullable && shouldOmitEmpty` explicitly prevented any nullable field from receiving `omitempty`, even when the field was optional (not required). This contradicted the documented behavior. The fix removes the `!p.Nullable` guard so nullable fields follow the same `omitempty` rules as non-nullable fields. The special-case exception for the `nullable-type` output option is no longer needed since the logic is now uniform. Note: `x-go-type-skip-optional-pointer: true` on a nullable field suppresses the pointer (generating `string` instead of `*string`) but still correctly receives `omitempty` when the field is optional. Whether skip-optional-pointer should be allowed to suppress the pointer on nullable fields is a separate concern. Co-Authored-By: Claude Opus 4.6 --- .../test/issues/issue-1039/defaultbehaviour/types.gen.go | 6 +++--- internal/test/schemas/schemas.gen.go | 2 +- pkg/codegen/codegen_test.go | 4 ++-- pkg/codegen/schema.go | 6 +----- 4 files changed, 7 insertions(+), 11 deletions(-) diff --git a/internal/test/issues/issue-1039/defaultbehaviour/types.gen.go b/internal/test/issues/issue-1039/defaultbehaviour/types.gen.go index b148f37830..5ed98d88f1 100644 --- a/internal/test/issues/issue-1039/defaultbehaviour/types.gen.go +++ b/internal/test/issues/issue-1039/defaultbehaviour/types.gen.go @@ -6,7 +6,7 @@ package defaultbehaviour // PatchRequest A request to patch an existing user object. type PatchRequest struct { // ComplexOptionalNullable Complex, optional and nullable - ComplexOptionalNullable *ComplexOptionalNullable `json:"complex_optional_nullable"` + ComplexOptionalNullable *ComplexOptionalNullable `json:"complex_optional_nullable,omitempty"` // ComplexRequiredNullable Complex required and nullable ComplexRequiredNullable *ComplexRequiredNullable `json:"complex_required_nullable"` @@ -15,7 +15,7 @@ type PatchRequest struct { SimpleOptionalNonNullable *SimpleOptionalNonNullable `json:"simple_optional_non_nullable,omitempty"` // SimpleOptionalNullable Simple optional and nullable - SimpleOptionalNullable *SimpleOptionalNullable `json:"simple_optional_nullable"` + SimpleOptionalNullable *SimpleOptionalNullable `json:"simple_optional_nullable,omitempty"` // SimpleRequiredNullable Simple required and nullable SimpleRequiredNullable *SimpleRequiredNullable `json:"simple_required_nullable"` @@ -24,7 +24,7 @@ type PatchRequest struct { // ComplexOptionalNullable Complex, optional and nullable type ComplexOptionalNullable struct { // AliasName Optional and nullable - AliasName *string `json:"alias_name"` + AliasName *string `json:"alias_name,omitempty"` // Name Optional and non nullable Name *string `json:"name,omitempty"` diff --git a/internal/test/schemas/schemas.gen.go b/internal/test/schemas/schemas.gen.go index 1a7f9e61b7..f9c7f75790 100644 --- a/internal/test/schemas/schemas.gen.go +++ b/internal/test/schemas/schemas.gen.go @@ -80,7 +80,7 @@ type GenericObject = map[string]interface{} // NullableProperties defines model for NullableProperties. type NullableProperties struct { Optional *string `json:"optional,omitempty"` - OptionalAndNullable *string `json:"optionalAndNullable"` + OptionalAndNullable *string `json:"optionalAndNullable,omitempty"` Required string `json:"required"` RequiredAndNullable *string `json:"requiredAndNullable"` } diff --git a/pkg/codegen/codegen_test.go b/pkg/codegen/codegen_test.go index 7b22faed01..1654a7e164 100644 --- a/pkg/codegen/codegen_test.go +++ b/pkg/codegen/codegen_test.go @@ -110,8 +110,8 @@ func TestExtPropGoTypeSkipOptionalPointer(t *testing.T) { assert.NoError(t, err) // Check that optional pointer fields are skipped if requested - assert.Contains(t, code, "NullableFieldSkipFalse *string `json:\"nullableFieldSkipFalse\"`") - assert.Contains(t, code, "NullableFieldSkipTrue string `json:\"nullableFieldSkipTrue\"`") + assert.Contains(t, code, "NullableFieldSkipFalse *string `json:\"nullableFieldSkipFalse,omitempty\"`") + assert.Contains(t, code, "NullableFieldSkipTrue string `json:\"nullableFieldSkipTrue,omitempty\"`") assert.Contains(t, code, "OptionalField *string `json:\"optionalField,omitempty\"`") assert.Contains(t, code, "OptionalFieldSkipFalse *string `json:\"optionalFieldSkipFalse,omitempty\"`") assert.Contains(t, code, "OptionalFieldSkipTrue string `json:\"optionalFieldSkipTrue,omitempty\"`") diff --git a/pkg/codegen/schema.go b/pkg/codegen/schema.go index 7435fa224d..519925edc6 100644 --- a/pkg/codegen/schema.go +++ b/pkg/codegen/schema.go @@ -759,11 +759,7 @@ func GenFieldsFromProperties(props []Property) []string { shouldOmitEmpty := (!p.Required || p.ReadOnly || p.WriteOnly) && (!p.Required || !p.ReadOnly || !globalState.options.Compatibility.DisableRequiredReadOnlyAsPointer) - omitEmpty := !p.Nullable && shouldOmitEmpty - - if p.Nullable && globalState.options.OutputOptions.NullableType { - omitEmpty = shouldOmitEmpty - } + omitEmpty := shouldOmitEmpty omitZero := false