Skip to content

[go] add useAnyOfAllMatches option for spec-compliant anyOf unmarshalling#23194

Open
AriehSchneier wants to merge 4 commits intoOpenAPITools:masterfrom
AriehSchneier:fix/go-anyof-unmarshal-all-matches
Open

[go] add useAnyOfAllMatches option for spec-compliant anyOf unmarshalling#23194
AriehSchneier wants to merge 4 commits intoOpenAPITools:masterfrom
AriehSchneier:fix/go-anyof-unmarshal-all-matches

Conversation

@AriehSchneier
Copy link

@AriehSchneier AriehSchneier commented Mar 10, 2026

Description

The anyOf specification requires that data validates against one or more of the subschemas. The current generated UnmarshalJSON for anyOf schemas returns on the first successful match, which means:

  • Subsequent schemas are never attempted
  • Multiple simultaneously-matching fields are not populated
  • Results are order-dependent (schema order in the spec determines which field wins)

This is the same pattern used by C# and Java generators, but it is not spec-compliant.

Fix

Add a new generator option useAnyOfAllMatches (default: false) that, when enabled, tries all anyOf schemas and populates every field whose schema matches. Returns nil if at least one schema matched, or an error if none did.

The default remains false to preserve backwards compatibility with existing generated code.

Changes

  • GoClientCodegen.java: add USE_ANYOF_ALL_MATCHES constant, useAnyOfAllMatches field, CLI option, processOpts handling, and getter
  • model_anyof.mustache: {{#useAnyOfAllMatches}} block emits a try-all + match-count loop; {{^useAnyOfAllMatches}} preserves existing first-match behaviour
  • GoClientOptionsProvider / GoClientOptionsTest: coverage for the new option
  • anyof_multiple_matches.yaml: test spec with two schemas (SpecA has name, SpecB has value) that can simultaneously match the same payload under a Contact anyOf
  • GoClientCodegenTest: two new tests verifying default behaviour is unchanged and new behaviour works correctly

PR checklist

  • Read the contribution guidelines
  • If contributing template-only or documentation-only changes which will change sample output, build the project before running ./bin/generate-samples.sh
  • File the PR against the correct branch
  • Add test(s) — two new tests in GoClientCodegenTest plus options test coverage
  • Generate samples for any changed language — N/A (behind opt-in flag, default unchanged)

Summary by cubic

Adds useAnyOfAllMatches to the Go generator for spec-compliant anyOf handling. When enabled, unmarshal tries all subschemas and populates every matching field; marshal merges object fields, errors on conflicting values, and falls back to the first non-object value. Default remains first-match.

  • New Features

    • New CLI/config option useAnyOfAllMatches (default: false): unmarshal tries all anyOf schemas; succeeds if at least one matches; populates all matching fields. Marshal merges object keys from all non-nil schemas, errors on conflicting values for the same key, and falls back to the first non-object value.
    • model_anyof.mustache: adds try-all loop in UnmarshalJSON and merged output with conflict detection and primitive fallback in MarshalJSON when enabled; preserves current first-match paths otherwise.
    • GoClientCodegen: adds option constant, CLI flag, processing, and getter. Tests cover default vs. enabled behavior, object merge with conflict detection (anyof_clashing_fields.yaml), primitive fallback (anyof_primitive_schemas.yaml), and multiple matches (anyof_multiple_matches.yaml).
  • Migration

    • Opt in via --additional-properties useAnyOfAllMatches=true. Default behavior is unchanged.

Written for commit e0a11c7. Summary will update on new commits.

…ling

Add useAnyOfAllMatches option (default false) to try all anyOf schemas
and populate every matching field, instead of returning on first match.
Default behaviour is unchanged. Includes tests and test YAML spec.
Copy link
Contributor

@cubic-dev-ai cubic-dev-ai bot left a comment

Choose a reason for hiding this comment

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

1 issue found across 6 files

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="modules/openapi-generator/src/main/resources/go/model_anyof.mustache">

<violation number="1" location="modules/openapi-generator/src/main/resources/go/model_anyof.mustache:62">
P1: `useAnyOfAllMatches` can populate multiple anyOf schema fields, but `MarshalJSON` still returns only the first non-nil schema, causing silent lossy, order-dependent re-serialization.</violation>
</file>

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.

When useAnyOfAllMatches=true, UnmarshalJSON can populate multiple anyOf
schema fields simultaneously. The previous MarshalJSON returned on the
first non-nil field, which silently discarded all other populated fields,
making round-trip serialization lossy and order-dependent.

Fix: under useAnyOfAllMatches, MarshalJSON marshals every non-nil schema
field to a map[string]interface{} and merges the results before encoding.
Non-object schemas (primitives, arrays) that cannot be merged are skipped.
The default (first-match) path is unchanged.
Copy link
Contributor

@cubic-dev-ai cubic-dev-ai bot left a comment

Choose a reason for hiding this comment

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

1 issue found across 2 files (changes from recent commits).

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="modules/openapi-generator/src/main/resources/go/model_anyof.mustache">

<violation number="1" location="modules/openapi-generator/src/main/resources/go/model_anyof.mustache:112">
P2: useAnyOfAllMatches silently drops non-object anyOf variants during MarshalJSON because it only treats map-unmarshal success as data, leading to data loss or nil output for primitive/array matches.</violation>
</file>

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.

…oss anyOf schemas

When multiple anyOf object schemas share the same field name and are both
populated, the merge loop now compares existing and incoming values by their
JSON representation. If they differ, MarshalJSON returns an error instead of
silently overwriting (last-write-wins), which would produce incorrect output.

Identical values for a shared key are allowed (merge succeeds normally).

Also adds:
- anyof_primitive_schemas.yaml test spec (string | integer anyOf)
- anyof_clashing_fields.yaml test spec (two object schemas sharing 'id' with
  different types)
- testAnyOfMarshalConflictingKeysReturnsError in GoClientCodegenTest
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.

1 participant