OpenAPITools / OpenAPITools/openapi-generator
[BUG][Go] No json tags emitted when a schema has both properties and a schema-level oneOf, silently dropping snake_case fields on decode
Nobody has claimed this yet.
- Dominant language
- Java
- Stars
- 26.8k
- Forks
- 7.7k
- PR merge metrics
- PR metrics pending
Description
Bug Report Checklist
- Have you provided a full/minimal spec to reproduce the issue?
- Have you validated the input using an OpenAPI validator?
- Have you tested with the latest master? — tested with the latest release, v7.25.0
- Have you searched for related issues/PRs?
- What's the actual output vs expected output?
Description
When a schema declares properties and a schema-level oneOf, the Go generator emits the model struct with no json tags at all. Go's decoder then falls back to matching field names case-insensitively, and that fallback does not ignore underscores — so every property whose wire name contains one is silently dropped on decode, while single-word properties decode fine.
This is worse than a cosmetic tag issue:
MarshalJSONis generated from a hand-builtToMap()that keeps the correct snake_case keys, so serialization stays healthy. Only deserialization loses data, and only for some fields.- The generated
UnmarshalJSONdeletes the known keys fromAdditionalProperties, so the value is not recoverable there either. - Tests that construct the model in Go and assert on its fields never touch the tags, so they stay green while decoding is broken. The failure surfaces only against a real payload.
The oneOf involved need not define variants. In the repro it is a pure co-occurrence constraint — "either first_value or second_value, not both" — which adds no shape and which the Go generator cannot express in the type system either way. It is valid OpenAPI, and it is the natural way to describe a schema that has gained a second mutually exclusive encoding, yet its mere presence costs the schema every tag.
openapi-generator version
7.25.0. Also reproduced on 7.18.0, so this is not a recent regression.
OpenAPI declaration file content
openapi: 3.0.3
info:
title: Tag Loss Repro
version: 1.0.0
paths:
/thing:
get:
operationId: getThing
responses:
'200':
description: OK
content:
application/json:
schema:
$ref: '#/components/schemas/Thing'
components:
schemas:
Thing:
type: object
required: [kind]
properties:
kind:
type: string
first_value:
type: array
items:
type: number
second_value:
type: array
items:
type: number
oneOf:
- required: [first_value]
not:
required: [second_value]
- required: [second_value]
not:
required: [first_value]
Generation details
docker run --rm -v "$PWD:/local" openapitools/openapi-generator-cli:v7.25.0 generate \
-i /local/spec.yaml -g go -o /local/out \
--additional-properties="withGoMod=false,packageName=client"
Actual
type Thing struct {
Kind string
FirstValue []float32
SecondValue []float32
}
Expected — and exactly what the generator produces from the same spec with the oneOf block removed, which isolates the trigger:
type Thing struct {
Kind string `json:"kind"`
FirstValue []float32 `json:"first_value,omitempty"`
SecondValue []float32 `json:"second_value,omitempty"`
}
With the oneOf present, json.Unmarshal of {"kind":"example","first_value":[1,2],"second_value":[3,4]} leaves both slices empty while Kind is populated.
Related
#21461 reports missing omitempty on the members of a pure oneOf union. Possibly the same code path classifying any schema carrying oneOf as a union model, but the symptom and blast radius differ: that one loses an omitempty on a wrapper, this one loses every tag on an ordinary model and drops data.
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start with spec.yaml and reproduce the issue using the documented Docker command and Go generator, then trace how schemas containing both properties and oneOf are classified. Done means generated models retain their json tags and a real json.Unmarshal regression case preserves first_value and second_value.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- go, openapi
- Domain
- tooling
- Issue type
- Bug
- Difficulty
- 3/5
- Estimated time
- 1-2 days
- Activity status
- Active
- Clarity
- Clearly specified
- Newbie friendliness
- 74/100