Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Java Generator Configuration

The java-okhttp backend emits Java 17 sealed interfaces and records for unions, with Gson adapters that retain the selected member’s declared payload.

Untagged union selection

[java-okhttp]
package_name = "com.example.sdk"
untagged_union_strategy = "firstMatch"
PolicyBehavior
firstMatch (default)Keep typed variants; choose the first structural match in schema order, with concrete members before opaque fallbacks.
strictKeep the same typed API; reject multiple authored concrete matches before decoding. Use an opaque fallback only when no concrete member matches.
rawExplicitly emit only VariantRaw(JsonElement), preserving known, ambiguous, unmatched, and null JSON without typed selection.

Both typed policies reject unmatched non-null values. Matching uses required wire keys, finite scalar constraints (including numeric and boolean enums), nested arrays and maps, and productive recursive references. Finite numbers use exact BigDecimal significands and BigInteger exponents, retaining distinctions above double precision without Gson’s decimal-accessor scale limit. Recursive checks share results by JSON node identity and predicate within one selection, not across decoded values. Private recursive-cache types are qualified so models named Integer or Boolean do not shadow the runtime’s boxed types.

After selection, the original member is decoded once through its existing Gson codec. Its errors, including nested strict-union errors, propagate rather than being hidden by another member or opaque fallback. This is structural selection, not full JSON Schema validation: formats, numeric bounds, and ordinary Gson coercion or overflow behavior are not redesigned. Serialization does not add schema validation.

Null and representation compatibility

Typed policies preserve Java’s existing root-null API: JSON null becomes Java null for a nonnullable union, or the existing VariantNull.INSTANCE for a nullable union. No synthetic raw variant is added to typed unions. Explicit raw mode instead wraps JSON null in VariantRaw(JsonNull.INSTANCE). As with typed cases, a case name equal to its enclosing interface is disambiguated (for example, VariantRaw.VariantRawVariant).

Java aliases remain objects with a value field. Compatible allOf models now have flat object codecs, described below; union matchers follow that flat wire shape rather than the source-facing member names. Tagged unions and ordinary models keep their existing APIs and codecs.

Typed variants retain declared member payloads, not arbitrary unknown fields. Explicit additional-property storage is described below. Use raw mode when all JSON must be retained, including unknown keys, null-valued object entries, and large numeric literals. Raw writing restores the surrounding Gson writer’s null policy; ordinary sibling model fields keep their normal null omission.

The policy can also be set through the CLI:

openapi-nexus generate --input api.yaml --output sdk --generators java-okhttp \
  --generator-config java-okhttp.untagged_union_strategy=strict

Additional properties

Objects with explicit additionalProperties expose typed, mutable storage through getAdditionalProperties(). This includes pure dictionaries with no named fields; they remain ordinary model classes rather than Map subtypes. Existing named constructor parameters and getters stay unchanged, with an independent empty map available on each constructed or decoded object.

MixedMap value = new MixedMap("fixed");
value.getAdditionalProperties().put("keep_key", new NestedValue("nested"));
// Gson writes {"known_field":"fixed","keep_key":{"wire_value":"nested"}}

The Gson adapter flattens extensions into the same JSON object, retaining keys verbatim. Opaque values (true or {}) use JsonElement so null entries and large numeric literals survive without coercion to Object/double. Declared typed values use their native Gson adapters, including model/enum/nullable values, containers, recursive references and configured untagged-union policies.

Storage is transient to the ordinary Gson model delegate. Known fields retain their configured codecs, native scalar failures and null omission; extension entries preserve their own nulls without changing surrounding writer settings. The runtime distinguishes Gson’s streaming and tree readers and uses its native tree writer so their existing coercion and configured numeric output stay intact. This support depends on the tree reader/writer classes in the unchanged Gson 2.11.0 dependency; native contracts cover both input modes. Aliases keep their existing value wrapper instead of acquiring a different wire shape. No generated dependency version or general validation policy changes.

If an authored field/getter already uses the storage name, the storage and getter receive a numeric suffix, such as additionalProperties2 and getAdditionalProperties2(). Authored members are not renamed. An extension key equal to a declared wire key raises IllegalArgumentException on serialization, even if the named field is currently omitted, rather than silently overwriting it.

Closed objects and those without explicit additional properties keep their existing behavior. Shared lowering, general inline-composition type loss, multi-member allOf, tagged-wrapper extension storage and generation formatting are separate concerns, not redesigned by this object-storage repair.

allOf composition

[java-okhttp]
all_of_strategy = "composed"

The default composed policy retains compatible object/map member constructor arguments and getters, but reads and writes a single flat JSON object:

MixedAlias value = new MixedAlias(new MixedMap("fixed"));
// Gson writes {"known_field":"fixed"}, not {"mixedMap":{...}}.
value.getMixedMap(); // The source-facing member entrypoint remains available.

Members are decoded once through the configured Gson instance. Named model members keep their ordinary codecs, field naming, null omission and native failures. Projection excludes sibling declared keys from extension storage while retaining input order, repeated keys and numeric lexemes, including inside nested values. It preserves the distinction between streaming and tree input modes. Nested compositions and repeated members retain their member APIs; conflicting member write encodings raise IllegalArgumentException instead of losing data.

Anonymous dictionary members retain their Map source-facing types. Their opaque Object entries decode as JsonElement values (with Java null for JSON null) so large numbers and null entries survive. Typed dictionary values use configured leaf adapters; containers retain explicit null entries. This is not a change to ordinary Object fields or ordinary dictionary model codecs.

This is conservative representation planning, not full JSON Schema validation. Non-object, nullable, incompatible, cyclic and ordinary-wrapper-alias members use a raw representation rather than guessing a flat typed codec. Ordinary alias codecs themselves remain unchanged. Missing shared-lowering facts and implicit additional-property storage are not inferred. Nullable members conservatively choose raw even alongside a non-nullable sibling.

Set all_of_strategy = "raw" to use raw representation for every intersection:

openapi-nexus generate --input api.yaml --output sdk --generators java-okhttp \
  --generator-config java-okhttp.all_of_strategy=raw

A raw allOf model has a JsonElement constructor and getValue() accessor. It retains arbitrary JSON kinds, nulls and numeric literals without enforcing intersection constraints. Consumers of formerly unsupported or nested-wrapper representations must regenerate and adjust construction/serialization code; no binary-compatibility claim is made. Successful composed member APIs are retained. AllOf selection is independent of untagged_union_strategy.

The private runtime uses Gson 2.11.0’s JsonTreeReader, JsonTreeWriter and LazilyParsedNumber implementation classes to preserve input modes, configured numeric output and lexemes. Generated dependencies remain unchanged. Kotlin composition, ordinary alias repair and generation formatting remain separate.

An existing typed-union writer limitation can omit explicit null entries when it re-emits a selected member’s JSON tree, including ordinary open objects without any allOf. Flat matcher support does not repair that surrounding writer boundary; allOf model, ordinary containing-object and HTTP codec behavior are tested independently of it.