diff --git a/bin/configs/java-okhttp-gson.yaml b/bin/configs/java-okhttp-gson.yaml index 88b87d75c4a..38b017cb325 100644 --- a/bin/configs/java-okhttp-gson.yaml +++ b/bin/configs/java-okhttp-gson.yaml @@ -6,3 +6,4 @@ templateDir: modules/openapi-generator/src/main/resources/Java additionalProperties: artifactId: petstore-okhttp-gson hideGenerationTimestamp: "true" + useOneOfDiscriminatorLookup: "true" diff --git a/modules/openapi-generator/src/main/resources/Java/libraries/okhttp-gson/oneof_model.mustache b/modules/openapi-generator/src/main/resources/Java/libraries/okhttp-gson/oneof_model.mustache index 0e197ed70a8..1e7748d0ed2 100644 --- a/modules/openapi-generator/src/main/resources/Java/libraries/okhttp-gson/oneof_model.mustache +++ b/modules/openapi-generator/src/main/resources/Java/libraries/okhttp-gson/oneof_model.mustache @@ -76,16 +76,20 @@ public class {{classname}} extends AbstractOpenApiSchema{{#vendorExtensions.x-im {{#discriminator}} // use discriminator value for faster oneOf lookup {{classname}} new{{classname}} = new {{classname}}(); - String discriminatorValue = elementAdapter.read(in).getAsJsonObject().get("{{{propertyBaseName}}}").getAsString(); - switch (discriminatorValue) { - {{#mappedModels}} - case "{{{mappingName}}}": - deserialized = adapter{{modelName}}.fromJsonTree(jsonObject); - new{{classname}}.setActualInstance(deserialized); - return new{{classname}}; - {{/mappedModels}} - default: - log.log(Level.WARNING, String.format("Failed to lookup discriminator value `%s` for {{classname}}. Possible values:{{#mappedModels}} {{{mappingName}}}{{/mappedModels}}", discriminatorValue)); + if (jsonObject.get("{{{propertyBaseName}}}") == null) { + log.log(Level.WARNING, "Failed to lookup discriminator value for {{classname}} as `{{{propertyBaseName}}}` was not found in the payload or the payload is empty."); + } else { + // look up the discriminator value in the field `{{{propertyBaseName}}}` + switch (jsonObject.get("{{{propertyBaseName}}}").getAsString()) { + {{#mappedModels}} + case "{{{mappingName}}}": + deserialized = adapter{{modelName}}.fromJsonTree(jsonObject); + new{{classname}}.setActualInstance(deserialized); + return new{{classname}}; + {{/mappedModels}} + default: + log.log(Level.WARNING, String.format("Failed to lookup discriminator value `%s` for {{classname}}. Possible values:{{#mappedModels}} {{{mappingName}}}{{/mappedModels}}", jsonObject.get("{{{propertyBaseName}}}").getAsString())); + } } {{/discriminator}} diff --git a/samples/client/petstore/java/okhttp-gson/src/main/java/org/openapitools/client/model/Mammal.java b/samples/client/petstore/java/okhttp-gson/src/main/java/org/openapitools/client/model/Mammal.java index e03d92e9f47..fe4771cc4cf 100644 --- a/samples/client/petstore/java/okhttp-gson/src/main/java/org/openapitools/client/model/Mammal.java +++ b/samples/client/petstore/java/okhttp-gson/src/main/java/org/openapitools/client/model/Mammal.java @@ -113,6 +113,30 @@ public class Mammal extends AbstractOpenApiSchema { Object deserialized = null; JsonObject jsonObject = elementAdapter.read(in).getAsJsonObject(); + // use discriminator value for faster oneOf lookup + Mammal newMammal = new Mammal(); + if (jsonObject.get("className") == null) { + log.log(Level.WARNING, "Failed to lookup discriminator value for Mammal as `className` was not found in the payload or the payload is empty."); + } else { + // look up the discriminator value in the field `className` + switch (jsonObject.get("className").getAsString()) { + case "Pig": + deserialized = adapterPig.fromJsonTree(jsonObject); + newMammal.setActualInstance(deserialized); + return newMammal; + case "whale": + deserialized = adapterWhale.fromJsonTree(jsonObject); + newMammal.setActualInstance(deserialized); + return newMammal; + case "zebra": + deserialized = adapterZebra.fromJsonTree(jsonObject); + newMammal.setActualInstance(deserialized); + return newMammal; + default: + log.log(Level.WARNING, String.format("Failed to lookup discriminator value `%s` for Mammal. Possible values: Pig whale zebra", jsonObject.get("className").getAsString())); + } + } + int match = 0; TypeAdapter actualAdapter = elementAdapter; diff --git a/samples/client/petstore/java/okhttp-gson/src/main/java/org/openapitools/client/model/NullableShape.java b/samples/client/petstore/java/okhttp-gson/src/main/java/org/openapitools/client/model/NullableShape.java index ded7d140bb9..beb9bf6f6e4 100644 --- a/samples/client/petstore/java/okhttp-gson/src/main/java/org/openapitools/client/model/NullableShape.java +++ b/samples/client/petstore/java/okhttp-gson/src/main/java/org/openapitools/client/model/NullableShape.java @@ -104,6 +104,26 @@ public class NullableShape extends AbstractOpenApiSchema { Object deserialized = null; JsonObject jsonObject = elementAdapter.read(in).getAsJsonObject(); + // use discriminator value for faster oneOf lookup + NullableShape newNullableShape = new NullableShape(); + if (jsonObject.get("shapeType") == null) { + log.log(Level.WARNING, "Failed to lookup discriminator value for NullableShape as `shapeType` was not found in the payload or the payload is empty."); + } else { + // look up the discriminator value in the field `shapeType` + switch (jsonObject.get("shapeType").getAsString()) { + case "Quadrilateral": + deserialized = adapterQuadrilateral.fromJsonTree(jsonObject); + newNullableShape.setActualInstance(deserialized); + return newNullableShape; + case "Triangle": + deserialized = adapterTriangle.fromJsonTree(jsonObject); + newNullableShape.setActualInstance(deserialized); + return newNullableShape; + default: + log.log(Level.WARNING, String.format("Failed to lookup discriminator value `%s` for NullableShape. Possible values: Quadrilateral Triangle", jsonObject.get("shapeType").getAsString())); + } + } + int match = 0; TypeAdapter actualAdapter = elementAdapter; diff --git a/samples/client/petstore/java/okhttp-gson/src/main/java/org/openapitools/client/model/Pig.java b/samples/client/petstore/java/okhttp-gson/src/main/java/org/openapitools/client/model/Pig.java index 75011050dfc..54bbc3b6e18 100644 --- a/samples/client/petstore/java/okhttp-gson/src/main/java/org/openapitools/client/model/Pig.java +++ b/samples/client/petstore/java/okhttp-gson/src/main/java/org/openapitools/client/model/Pig.java @@ -104,6 +104,26 @@ public class Pig extends AbstractOpenApiSchema { Object deserialized = null; JsonObject jsonObject = elementAdapter.read(in).getAsJsonObject(); + // use discriminator value for faster oneOf lookup + Pig newPig = new Pig(); + if (jsonObject.get("className") == null) { + log.log(Level.WARNING, "Failed to lookup discriminator value for Pig as `className` was not found in the payload or the payload is empty."); + } else { + // look up the discriminator value in the field `className` + switch (jsonObject.get("className").getAsString()) { + case "BasquePig": + deserialized = adapterBasquePig.fromJsonTree(jsonObject); + newPig.setActualInstance(deserialized); + return newPig; + case "DanishPig": + deserialized = adapterDanishPig.fromJsonTree(jsonObject); + newPig.setActualInstance(deserialized); + return newPig; + default: + log.log(Level.WARNING, String.format("Failed to lookup discriminator value `%s` for Pig. Possible values: BasquePig DanishPig", jsonObject.get("className").getAsString())); + } + } + int match = 0; TypeAdapter actualAdapter = elementAdapter; diff --git a/samples/client/petstore/java/okhttp-gson/src/main/java/org/openapitools/client/model/Quadrilateral.java b/samples/client/petstore/java/okhttp-gson/src/main/java/org/openapitools/client/model/Quadrilateral.java index 3232c7918c0..61b9d62aa02 100644 --- a/samples/client/petstore/java/okhttp-gson/src/main/java/org/openapitools/client/model/Quadrilateral.java +++ b/samples/client/petstore/java/okhttp-gson/src/main/java/org/openapitools/client/model/Quadrilateral.java @@ -104,6 +104,26 @@ public class Quadrilateral extends AbstractOpenApiSchema { Object deserialized = null; JsonObject jsonObject = elementAdapter.read(in).getAsJsonObject(); + // use discriminator value for faster oneOf lookup + Quadrilateral newQuadrilateral = new Quadrilateral(); + if (jsonObject.get("quadrilateralType") == null) { + log.log(Level.WARNING, "Failed to lookup discriminator value for Quadrilateral as `quadrilateralType` was not found in the payload or the payload is empty."); + } else { + // look up the discriminator value in the field `quadrilateralType` + switch (jsonObject.get("quadrilateralType").getAsString()) { + case "ComplexQuadrilateral": + deserialized = adapterComplexQuadrilateral.fromJsonTree(jsonObject); + newQuadrilateral.setActualInstance(deserialized); + return newQuadrilateral; + case "SimpleQuadrilateral": + deserialized = adapterSimpleQuadrilateral.fromJsonTree(jsonObject); + newQuadrilateral.setActualInstance(deserialized); + return newQuadrilateral; + default: + log.log(Level.WARNING, String.format("Failed to lookup discriminator value `%s` for Quadrilateral. Possible values: ComplexQuadrilateral SimpleQuadrilateral", jsonObject.get("quadrilateralType").getAsString())); + } + } + int match = 0; TypeAdapter actualAdapter = elementAdapter; diff --git a/samples/client/petstore/java/okhttp-gson/src/main/java/org/openapitools/client/model/Shape.java b/samples/client/petstore/java/okhttp-gson/src/main/java/org/openapitools/client/model/Shape.java index 90017b5e323..8c0c9b2fc8b 100644 --- a/samples/client/petstore/java/okhttp-gson/src/main/java/org/openapitools/client/model/Shape.java +++ b/samples/client/petstore/java/okhttp-gson/src/main/java/org/openapitools/client/model/Shape.java @@ -104,6 +104,26 @@ public class Shape extends AbstractOpenApiSchema { Object deserialized = null; JsonObject jsonObject = elementAdapter.read(in).getAsJsonObject(); + // use discriminator value for faster oneOf lookup + Shape newShape = new Shape(); + if (jsonObject.get("shapeType") == null) { + log.log(Level.WARNING, "Failed to lookup discriminator value for Shape as `shapeType` was not found in the payload or the payload is empty."); + } else { + // look up the discriminator value in the field `shapeType` + switch (jsonObject.get("shapeType").getAsString()) { + case "Quadrilateral": + deserialized = adapterQuadrilateral.fromJsonTree(jsonObject); + newShape.setActualInstance(deserialized); + return newShape; + case "Triangle": + deserialized = adapterTriangle.fromJsonTree(jsonObject); + newShape.setActualInstance(deserialized); + return newShape; + default: + log.log(Level.WARNING, String.format("Failed to lookup discriminator value `%s` for Shape. Possible values: Quadrilateral Triangle", jsonObject.get("shapeType").getAsString())); + } + } + int match = 0; TypeAdapter actualAdapter = elementAdapter; diff --git a/samples/client/petstore/java/okhttp-gson/src/main/java/org/openapitools/client/model/ShapeOrNull.java b/samples/client/petstore/java/okhttp-gson/src/main/java/org/openapitools/client/model/ShapeOrNull.java index 8ef34191d39..28badafc039 100644 --- a/samples/client/petstore/java/okhttp-gson/src/main/java/org/openapitools/client/model/ShapeOrNull.java +++ b/samples/client/petstore/java/okhttp-gson/src/main/java/org/openapitools/client/model/ShapeOrNull.java @@ -104,6 +104,26 @@ public class ShapeOrNull extends AbstractOpenApiSchema { Object deserialized = null; JsonObject jsonObject = elementAdapter.read(in).getAsJsonObject(); + // use discriminator value for faster oneOf lookup + ShapeOrNull newShapeOrNull = new ShapeOrNull(); + if (jsonObject.get("shapeType") == null) { + log.log(Level.WARNING, "Failed to lookup discriminator value for ShapeOrNull as `shapeType` was not found in the payload or the payload is empty."); + } else { + // look up the discriminator value in the field `shapeType` + switch (jsonObject.get("shapeType").getAsString()) { + case "Quadrilateral": + deserialized = adapterQuadrilateral.fromJsonTree(jsonObject); + newShapeOrNull.setActualInstance(deserialized); + return newShapeOrNull; + case "Triangle": + deserialized = adapterTriangle.fromJsonTree(jsonObject); + newShapeOrNull.setActualInstance(deserialized); + return newShapeOrNull; + default: + log.log(Level.WARNING, String.format("Failed to lookup discriminator value `%s` for ShapeOrNull. Possible values: Quadrilateral Triangle", jsonObject.get("shapeType").getAsString())); + } + } + int match = 0; TypeAdapter actualAdapter = elementAdapter; diff --git a/samples/client/petstore/java/okhttp-gson/src/main/java/org/openapitools/client/model/Triangle.java b/samples/client/petstore/java/okhttp-gson/src/main/java/org/openapitools/client/model/Triangle.java index eaac8b16856..3dea796aea1 100644 --- a/samples/client/petstore/java/okhttp-gson/src/main/java/org/openapitools/client/model/Triangle.java +++ b/samples/client/petstore/java/okhttp-gson/src/main/java/org/openapitools/client/model/Triangle.java @@ -113,6 +113,30 @@ public class Triangle extends AbstractOpenApiSchema { Object deserialized = null; JsonObject jsonObject = elementAdapter.read(in).getAsJsonObject(); + // use discriminator value for faster oneOf lookup + Triangle newTriangle = new Triangle(); + if (jsonObject.get("triangleType") == null) { + log.log(Level.WARNING, "Failed to lookup discriminator value for Triangle as `triangleType` was not found in the payload or the payload is empty."); + } else { + // look up the discriminator value in the field `triangleType` + switch (jsonObject.get("triangleType").getAsString()) { + case "EquilateralTriangle": + deserialized = adapterEquilateralTriangle.fromJsonTree(jsonObject); + newTriangle.setActualInstance(deserialized); + return newTriangle; + case "IsoscelesTriangle": + deserialized = adapterIsoscelesTriangle.fromJsonTree(jsonObject); + newTriangle.setActualInstance(deserialized); + return newTriangle; + case "ScaleneTriangle": + deserialized = adapterScaleneTriangle.fromJsonTree(jsonObject); + newTriangle.setActualInstance(deserialized); + return newTriangle; + default: + log.log(Level.WARNING, String.format("Failed to lookup discriminator value `%s` for Triangle. Possible values: EquilateralTriangle IsoscelesTriangle ScaleneTriangle", jsonObject.get("triangleType").getAsString())); + } + } + int match = 0; TypeAdapter actualAdapter = elementAdapter; diff --git a/samples/client/petstore/java/okhttp-gson/src/test/java/org/openapitools/client/JSONTest.java b/samples/client/petstore/java/okhttp-gson/src/test/java/org/openapitools/client/JSONTest.java index ab06507b40d..27fe00abbec 100644 --- a/samples/client/petstore/java/okhttp-gson/src/test/java/org/openapitools/client/JSONTest.java +++ b/samples/client/petstore/java/okhttp-gson/src/test/java/org/openapitools/client/JSONTest.java @@ -364,6 +364,65 @@ public class JSONTest { } } + /** + * Validate a oneOf schema can be deserialized into the expected class. + * The oneOf schema has a discriminator. + */ + @Test + public void testOneOfSchemaWithDiscriminator() throws Exception { + { + String str = "{ \"className\": \"whale\", \"hasBaleen\": false, \"hasTeeth\": false }"; + + // make sure deserialization works for pojo object + Whale w = json.getGson().fromJson(str, Whale.class); + assertEquals(w.getClassName(), "whale"); + assertEquals(w.getHasBaleen(), false); + assertEquals(w.getHasTeeth(), false); + + Mammal o = json.getGson().fromJson(str, Mammal.class); + assertTrue(o.getActualInstance() instanceof Whale); + Whale inst = (Whale) o.getActualInstance(); + assertEquals(inst.getClassName(), "whale"); + assertEquals(inst.getHasBaleen(), false); + assertEquals(inst.getHasTeeth(), false); + assertEquals(json.getGson().toJson(inst), "{\"hasBaleen\":false,\"hasTeeth\":false,\"className\":\"whale\"}"); + assertEquals(inst.toJson(), "{\"hasBaleen\":false,\"hasTeeth\":false,\"className\":\"whale\"}"); + assertEquals(json.getGson().toJson(o), "{\"hasBaleen\":false,\"hasTeeth\":false,\"className\":\"whale\"}"); + assertEquals(o.toJson(), "{\"hasBaleen\":false,\"hasTeeth\":false,\"className\":\"whale\"}"); + + String str2 = "{ \"className\": \"zebra\", \"type\": \"plains\" }"; + + // make sure deserialization works for pojo object + Zebra z = Zebra.fromJson(str2); + assertEquals(z.toJson(), "{\"className\":\"zebra\",\"type\":\"plains\"}"); + + Mammal o2 = json.getGson().fromJson(str2, Mammal.class); + assertTrue(o2.getActualInstance() instanceof Zebra); + Zebra inst2 = (Zebra) o2.getActualInstance(); + assertEquals(json.getGson().toJson(inst2), "{\"className\":\"zebra\",\"type\":\"plains\"}"); + assertEquals(inst2.toJson(), "{\"className\":\"zebra\",\"type\":\"plains\"}"); + assertEquals(json.getGson().toJson(o2), "{\"className\":\"zebra\",\"type\":\"plains\"}"); + assertEquals(o2.toJson(), "{\"className\":\"zebra\",\"type\":\"plains\"}"); + } + { + // incorrect payload results in exception + String str = "{ \"cultivar\": \"golden delicious\", \"mealy\": false, \"garbage_prop\": \"abc\" }"; + Exception exception = assertThrows(com.google.gson.JsonSyntaxException.class, () -> { + Mammal o = json.getGson().fromJson(str, Mammal.class); + }); + assertTrue(exception.getMessage().contains("Failed deserialization for Mammal: 0 classes match result, expected 1. JSON: {\"cultivar\":\"golden delicious\",\"mealy\":false,\"garbage_prop\":\"abc\"}")); + } + { + // Try to deserialize empty object. This should fail 'oneOf' because none will match + // whale or zebra. + String str = "{ }"; + Exception exception = assertThrows(com.google.gson.JsonSyntaxException.class, () -> { + json.getGson().fromJson(str, Mammal.class); + }); + assertTrue(exception.getMessage().contains("Failed deserialization for Mammal: 0 classes match result, expected 1")); + } + } + /** * Validate a oneOf schema can be deserialized into the expected class. * The oneOf schema does not have a discriminator.