Skip to content

Commit 7a3905f

Browse files
rar91279claude
andcommitted
[java][okhttp] Handle JSON-B parents without a discriminator
A model with children but no discriminator (x-parent, or a single allOf $ref) got neither the leaf (de)serializers, which skip every model with children, nor the hierarchy-root ones, which required a discriminator. It fell back to plain bean mapping and lost its required-field checks and additionalProperties. The root (de)serializers now cover every parent; with no discriminator the deserializer binds the root itself. Subtypes still delegate to the polymorphism-free instance, so Yasson's superclass lookup cannot narrow them to the parent. No sample defines such a parent. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
1 parent 6a7029a commit 7a3905f

3 files changed

Lines changed: 24 additions & 6 deletions

File tree

‎modules/openapi-generator/src/main/resources/Java/libraries/okhttp/JSON.mustache‎

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -784,10 +784,8 @@ public class JSON {
784784
{{#model}}
785785
{{^isEnum}}
786786
{{#hasChildren}}
787-
{{#discriminator}}
788787
config.withSerializers(new {{modelPackage}}.{{classname}}.CustomJsonbSerializer());
789788
config.withDeserializers(new {{modelPackage}}.{{classname}}.CustomJsonbDeserializer());
790-
{{/discriminator}}
791789
{{/hasChildren}}
792790
{{/isEnum}}
793791
{{/model}}

‎modules/openapi-generator/src/main/resources/Java/libraries/okhttp/pojo.mustache‎

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,6 @@ import jakarta.json.bind.adapter.JsonbAdapter;
3131
import jakarta.json.bind.annotation.JsonbTransient;
3232
import jakarta.json.bind.annotation.JsonbTypeAdapter;
3333
{{#hasChildren}}
34-
{{#discriminator}}
3534
import jakarta.json.JsonObject;
3635
import jakarta.json.bind.JsonbException;
3736
import jakarta.json.bind.serializer.DeserializationContext;
@@ -41,7 +40,6 @@ import jakarta.json.bind.serializer.SerializationContext;
4140
import jakarta.json.stream.JsonGenerator;
4241
import jakarta.json.stream.JsonParser;
4342
import java.lang.reflect.Type;
44-
{{/discriminator}}
4543
{{/hasChildren}}
4644
{{#isAdditionalPropertiesTrue}}
4745
{{^hasChildren}}
@@ -788,7 +786,10 @@ public class {{classname}} {{#parent}}extends {{{.}}} {{/parent}}{{#vendorExtens
788786
{{/isGson}}
789787
{{#isJsonb}}
790788
{{#hasChildren}}
791-
{{#discriminator}}
789+
{{! A parent without a discriminator (x-parent, or a single allOf $ref) needs these too: the
790+
leaf (de)serializers below are skipped for every model with children, and Yasson falls
791+
back to a superclass' serializer, so only this delegating variant is safe for a parent.
792+
With no discriminator the deserializer simply binds the root itself. }}
792793
793794
/**
794795
* Custom JSON-B serializer for the root of a discriminated hierarchy. Yasson serializes a
@@ -886,7 +887,9 @@ public class {{classname}} {{#parent}}extends {{{.}}} {{/parent}}{{#vendorExtens
886887
@Override
887888
public {{classname}} deserialize(JsonParser parser, DeserializationContext context, Type rtType) {
888889
JsonObject jsonObj = parser.getObject();
890+
{{#discriminator}}
889891
String discriminatorValue = jsonObj.getString("{{{discriminator.propertyBaseName}}}", null);
892+
{{/discriminator}}
890893
{{#discriminator.mappedModels}}
891894
if ("{{mappingName}}".equals(discriminatorValue)) {
892895
{{#model}}
@@ -960,7 +963,6 @@ public class {{classname}} {{#parent}}extends {{{.}}} {{/parent}}{{#vendorExtens
960963
}
961964
}
962965
}
963-
{{/discriminator}}
964966
{{/hasChildren}}
965967
{{#isAdditionalPropertiesTrue}}
966968
{{^hasChildren}}

‎modules/openapi-generator/src/test/java/org/openapitools/codegen/java/JavaClientCodegenTest.java‎

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5739,6 +5739,24 @@ public void testOkhttpJsonbEmitsPolymorphismSerializers() {
57395739
assertThat(files.get("Cat.java")).content().doesNotContain("discriminatorValue");
57405740
}
57415741

5742+
@Test(description = "a JSON-B parent without a discriminator (x-parent) gets the delegating root "
5743+
+ "(de)serializers, since the leaf ones are skipped for every model with children")
5744+
public void testOkhttpJsonbParentWithoutDiscriminator() {
5745+
final Map<String, File> files = generateFromContract(
5746+
"src/test/resources/3_0/allOf_extension_parent.yaml",
5747+
JavaClientCodegen.OKHTTP,
5748+
Map.of(CodegenConstants.SERIALIZATION_LIBRARY, SERIALIZATION_LIBRARY_JSONB));
5749+
5750+
assertThat(files.get("Person.java")).content()
5751+
.contains("public static class CustomJsonbSerializer implements JsonbSerializer<Person>")
5752+
.contains("public static class CustomJsonbDeserializer implements JsonbDeserializer<Person>")
5753+
.contains("validateRequiredFields(jsonObj, openapiRequiredFields, \"Person\");")
5754+
.doesNotContain("discriminatorValue");
5755+
assertThat(files.get("JSON.java")).content()
5756+
.contains("config.withSerializers(new org.openapitools.client.model.Person.CustomJsonbSerializer());")
5757+
.contains("config.withDeserializers(new org.openapitools.client.model.Person.CustomJsonbDeserializer());");
5758+
}
5759+
57425760
@Test(description = "okhttp Jackson models import Arrays and Map only when the template uses them")
57435761
public void testOkhttpJacksonModelImportsArraysAndMapOnlyWhenUsed() {
57445762
final Map<String, File> files = generateFromContract(

0 commit comments

Comments
 (0)