fix anyOf in handling primitive types in java client (#16264)

This commit is contained in:
William Cheng 2023-08-06 13:48:13 +08:00 committed by GitHub
parent 1d4a6d713f
commit 5b2ceac93d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
24 changed files with 650 additions and 96 deletions

View File

@ -8,6 +8,7 @@ import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import com.google.gson.Gson;
@ -27,6 +28,7 @@ import com.google.gson.JsonSerializationContext;
import com.google.gson.JsonSerializer;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonArray;
import com.google.gson.JsonParseException;
import {{invokerPackage}}.JSON;
@ -43,9 +45,18 @@ public class {{classname}} extends AbstractOpenApiSchema{{#vendorExtensions.x-im
return null; // this class only serializes '{{classname}}' and its subtypes
}
final TypeAdapter<JsonElement> elementAdapter = gson.getAdapter(JsonElement.class);
{{#composedSchemas}}
{{#anyOf}}
final TypeAdapter<{{.}}> adapter{{.}} = gson.getDelegateAdapter(this, TypeToken.get({{.}}.class));
{{^isArray}}
final TypeAdapter<{{{dataType}}}> adapter{{{dataType}}} = gson.getDelegateAdapter(this, TypeToken.get({{{dataType}}}.class));
{{/isArray}}
{{#isArray}}
final Type typeInstance = new TypeToken<List<{{complexType}}>>(){}.getType();
final TypeAdapter<{{{dataType}}}> adapter{{complexType}}List = (TypeAdapter<List<{{complexType}}>>) gson.getDelegateAdapter(this, TypeToken.get(typeInstance));
{{/isArray}}
{{/anyOf}}
{{/composedSchemas}}
return (TypeAdapter<T>) new TypeAdapter<{{classname}}>() {
@Override
@ -55,16 +66,34 @@ public class {{classname}} extends AbstractOpenApiSchema{{#vendorExtensions.x-im
return;
}
{{#composedSchemas}}
{{#anyOf}}
// check if the actual instance is of the type `{{.}}`
if (value.getActualInstance() instanceof {{.}}) {
JsonObject obj = adapter{{.}}.toJsonTree(({{.}})value.getActualInstance()).getAsJsonObject();
elementAdapter.write(out, obj);
// check if the actual instance is of the type `{{{dataType}}}`
if (value.getActualInstance() instanceof {{#isArray}}List<?>{{/isArray}}{{^isArray}}{{{dataType}}}{{/isArray}}) {
{{#isPrimitiveType}}
JsonPrimitive primitive = adapter{{{dataType}}}.toJsonTree(({{{dataType}}})value.getActualInstance()).getAsJsonPrimitive();
elementAdapter.write(out, primitive);
return;
{{/isPrimitiveType}}
{{#isArray}}
List<?> list = (List<?>) value.getActualInstance();
if(list.get(0) instanceof {{complexType}}) {
JsonArray array = adapter{{{complexType}}}List.toJsonTree(({{{dataType}}})value.getActualInstance()).getAsJsonArray();
elementAdapter.write(out, array);
return;
}
{{/isArray}}
{{^isArray}}
{{^isPrimitiveType}}
JsonElement element = adapter{{{dataType}}}.toJsonTree(({{{dataType}}})value.getActualInstance());
elementAdapter.write(out, element);
return;
{{/isPrimitiveType}}
{{/isArray}}
}
{{/anyOf}}
throw new IOException("Failed to serialize as the type doesn't match anyOf schemas: {{#anyOf}}{{{.}}}{{^-last}}, {{/-last}}{{/anyOf}}");
{{/composedSchemas}}
throw new IOException("Failed to serialize as the type doesn't match anyOf schemae: {{#anyOf}}{{{.}}}{{^-last}}, {{/-last}}{{/anyOf}}");
}
@Override
@ -72,41 +101,93 @@ public class {{classname}} extends AbstractOpenApiSchema{{#vendorExtensions.x-im
Object deserialized = null;
JsonElement jsonElement = elementAdapter.read(in);
{{#useOneOfDiscriminatorLookup}}
{{#discriminator}}
// use discriminator value for faster anyOf lookup
{{classname}} new{{classname}} = new {{classname}}();
String discriminatorValue = elementAdapter.read(in).getAsJsonObject().get("{{{propertyBaseName}}}").getAsString();
switch (discriminatorValue) {
{{#mappedModels}}
case "{{{mappingName}}}":
deserialized = adapter{{modelName}}.fromJsonTree(jsonElement);
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));
}
ArrayList<String> errorMessages = new ArrayList<>();
TypeAdapter actualAdapter = elementAdapter;
{{/discriminator}}
{{/useOneOfDiscriminatorLookup}}
{{#composedSchemas}}
{{#anyOf}}
{{^hasVars}}
// deserialize {{{dataType}}}
try {
// validate the JSON object to see if any exception is thrown
{{#isNumber}}
if(!jsonElement.getAsJsonPrimitive().isNumber()) {
throw new IllegalArgumentException(String.format("Expected json element to be of type Number in the JSON string but got `%s`", jsonElement.toString()));
}
actualAdapter = adapter{{{dataType}}};
{{/isNumber}}
{{^isNumber}}
{{#isPrimitiveType}}
if(!jsonElement.getAsJsonPrimitive().is{{#isBoolean}}Boolean{{/isBoolean}}{{#isString}}String{{/isString}}{{^isString}}{{^isBoolean}}Number{{/isBoolean}}{{/isString}}()) {
throw new IllegalArgumentException(String.format("Expected json element to be of type {{#isBoolean}}Boolean{{/isBoolean}}{{#isString}}String{{/isString}}{{^isString}}{{^isBoolean}}Number{{/isBoolean}}{{/isString}} in the JSON string but got `%s`", jsonElement.toString()));
}
actualAdapter = adapter{{{dataType}}};
{{/isPrimitiveType}}
{{^isPrimitiveType}}
{{^isArray}}
{{{dataType}}}.validateJsonElement(jsonElement);
actualAdapter = adapter{{{dataType}}};
{{/isArray}}
{{/isPrimitiveType}}
{{/isNumber}}
{{#isArray}}
if (!jsonElement.isJsonArray()) {
throw new IllegalArgumentException(String.format("Expected json element to be a array type in the JSON string but got `%s`", jsonElement.toString()));
}
JsonArray array = jsonElement.getAsJsonArray();
// validate array items
for(JsonElement element : array) {
{{#items}}
{{#isNumber}}
if(!jsonElement.getAsJsonPrimitive().isNumber()) {
throw new IllegalArgumentException(String.format("Expected json element to be of type Number in the JSON string but got `%s`", jsonElement.toString()));
}
actualAdapter = adapter{{{dataType}}};
{{/isNumber}}
{{^isNumber}}
{{#isPrimitiveType}}
if(!element.getAsJsonPrimitive().is{{#isBoolean}}Boolean{{/isBoolean}}{{#isString}}String{{/isString}}{{^isString}}{{^isBoolean}}Number{{/isBoolean}}{{/isString}}()) {
throw new IllegalArgumentException(String.format("Expected array items to be of type {{#isBoolean}}Boolean{{/isBoolean}}{{#isString}}String{{/isString}}{{^isString}}{{^isBoolean}}Number{{/isBoolean}}{{/isString}} in the JSON string but got `%s`", jsonElement.toString()));
}
{{/isPrimitiveType}}
{{^isPrimitiveType}}
{{{dataType}}}.validateJsonElement(element);
{{/isPrimitiveType}}
{{/isNumber}}
{{/items}}
}
actualAdapter = adapter{{{complexType}}}List;
{{/isArray}}
{{classname}} ret = new {{classname}}();
ret.setActualInstance(actualAdapter.fromJsonTree(jsonElement));
return ret;
} catch (Exception e) {
// deserialization failed, continue
errorMessages.add(String.format("Deserialization for {{{dataType}}} failed with `%s`.", e.getMessage()));
log.log(Level.FINER, "Input data does not match schema '{{{dataType}}}'", e);
}
{{/hasVars}}
{{#hasVars}}
// deserialize {{{.}}}
try {
// validate the JSON object to see if any exception is thrown
{{.}}.validateJsonElement(jsonElement);
log.log(Level.FINER, "Input data matches schema '{{{.}}}'");
actualAdapter = adapter{{.}};
{{classname}} ret = new {{classname}}();
ret.setActualInstance(adapter{{.}}.fromJsonTree(jsonElement));
ret.setActualInstance(actualAdapter.fromJsonTree(jsonElement));
return ret;
log.log(Level.FINER, "Input data matches schema '{{{.}}}'");
} catch (Exception e) {
// deserialization failed, continue
errorMessages.add(String.format("Deserialization for {{{.}}} failed with `%s`.", e.getMessage()));
log.log(Level.FINER, "Input data does not match schema '{{{.}}}'", e);
}
{{/hasVars}}
{{/anyOf}}
{{/composedSchemas}}
throw new IOException(String.format("Failed deserialization for {{classname}}: no class matched. JSON: %s", jsonElement.toString()));
throw new IOException(String.format("Failed deserialization for {{classname}}: no class matches result, expected at least 1. Detailed failure message for anyOf schemas: %s. JSON: %s", errorMessages, jsonElement.toString()));
}
}.nullSafe();
}
@ -127,9 +208,11 @@ public class {{classname}} extends AbstractOpenApiSchema{{#vendorExtensions.x-im
{{/anyOf}}
static {
{{#composedSchemas}}
{{#anyOf}}
schemas.put("{{{.}}}", {{{.}}}.class);
schemas.put("{{{dataType}}}", {{{baseType}}}.class);
{{/anyOf}}
{{/composedSchemas}}
}
@Override
@ -143,7 +226,6 @@ public class {{classname}} extends AbstractOpenApiSchema{{#vendorExtensions.x-im
* {{#anyOf}}{{{.}}}{{^-last}}, {{/-last}}{{/anyOf}}
*
* It could be an instance of the 'anyOf' schemas.
* The anyOf child schemas may themselves be a composed schema (allOf, anyOf, anyOf).
*/
@Override
public void setActualInstance(Object instance) {
@ -154,13 +236,24 @@ public class {{classname}} extends AbstractOpenApiSchema{{#vendorExtensions.x-im
}
{{/isNullable}}
{{#composedSchemas}}
{{#anyOf}}
if (instance instanceof {{{.}}}) {
if (instance instanceof {{#isArray}}List<?>{{/isArray}}{{^isArray}}{{{dataType}}}{{/isArray}}) {
{{#isArray}}
List<?> list = (List<?>) instance;
if(list.get(0) instanceof {{complexType}}) {
super.setActualInstance(instance);
return;
}
{{/isArray}}
{{^isArray}}
super.setActualInstance(instance);
return;
{{/isArray}}
}
{{/anyOf}}
{{/composedSchemas}}
throw new RuntimeException("Invalid instance type. Must be {{#anyOf}}{{{.}}}{{^-last}}, {{/-last}}{{/anyOf}}");
}
@ -175,19 +268,20 @@ public class {{classname}} extends AbstractOpenApiSchema{{#vendorExtensions.x-im
return super.getActualInstance();
}
{{#composedSchemas}}
{{#anyOf}}
/**
* Get the actual instance of `{{{.}}}`. If the actual instance is not `{{{.}}}`,
* Get the actual instance of `{{{dataType}}}`. If the actual instance is not `{{{dataType}}}`,
* the ClassCastException will be thrown.
*
* @return The actual instance of `{{{.}}}`
* @throws ClassCastException if the instance is not `{{{.}}}`
* @return The actual instance of `{{{dataType}}}`
* @throws ClassCastException if the instance is not `{{{dataType}}}`
*/
public {{{.}}} get{{{.}}}() throws ClassCastException {
return ({{{.}}})super.getActualInstance();
public {{{dataType}}} get{{#isArray}}{{complexType}}List{{/isArray}}{{^isArray}}{{{dataType}}}{{/isArray}}() throws ClassCastException {
return ({{{dataType}}})super.getActualInstance();
}
{{/anyOf}}
{{/composedSchemas}}
/**
* Validates the JSON Element and throws an exception if issues found
@ -197,20 +291,69 @@ public class {{classname}} extends AbstractOpenApiSchema{{#vendorExtensions.x-im
*/
public static void validateJsonElement(JsonElement jsonElement) throws IOException {
// validate anyOf schemas one by one
int validCount = 0;
ArrayList<String> errorMessages = new ArrayList<>();
{{#composedSchemas}}
{{#anyOf}}
// validate the json string with {{{.}}}
// validate the json string with {{{dataType}}}
try {
{{^hasVars}}
{{#isNumber}}
if(!jsonElement.getAsJsonPrimitive().isNumber()) {
throw new IllegalArgumentException(String.format("Expected json element to be of type Number in the JSON string but got `%s`", jsonElement.toString()));
}
{{/isNumber}}
{{^isNumber}}
{{#isPrimitiveType}}
if(!jsonElement.getAsJsonPrimitive().is{{#isBoolean}}Boolean{{/isBoolean}}{{#isString}}String{{/isString}}{{^isString}}{{^isBoolean}}Number{{/isBoolean}}{{/isString}}()) {
throw new IllegalArgumentException(String.format("Expected json element to be of type {{#isBoolean}}Boolean{{/isBoolean}}{{#isString}}String{{/isString}}{{^isString}}{{^isBoolean}}Number{{/isBoolean}}{{/isString}} in the JSON string but got `%s`", jsonElement.toString()));
}
{{/isPrimitiveType}}
{{^isPrimitiveType}}
{{^isArray}}
{{{dataType}}}.validateJsonElement(jsonElement);
{{/isArray}}
{{/isPrimitiveType}}
{{/isNumber}}
{{#isArray}}
if (!jsonElement.isJsonArray()) {
throw new IllegalArgumentException(String.format("Expected json element to be a array type in the JSON string but got `%s`", jsonElement.toString()));
}
JsonArray array = jsonElement.getAsJsonArray();
// validate array items
for(JsonElement element : array) {
{{#items}}
{{#isNumber}}
if(!jsonElement.getAsJsonPrimitive().isNumber()) {
throw new IllegalArgumentException(String.format("Expected json element to be of type Number in the JSON string but got `%s`", jsonElement.toString()));
}
{{/isNumber}}
{{^isNumber}}
{{#isPrimitiveType}}
if(!element.getAsJsonPrimitive().is{{#isBoolean}}Boolean{{/isBoolean}}{{#isString}}String{{/isString}}{{^isString}}{{^isBoolean}}Number{{/isBoolean}}{{/isString}}()) {
throw new IllegalArgumentException(String.format("Expected array items to be of type {{#isBoolean}}Boolean{{/isBoolean}}{{#isString}}String{{/isString}}{{^isString}}{{^isBoolean}}Number{{/isBoolean}}{{/isString}} in the JSON string but got `%s`", jsonElement.toString()));
}
{{/isPrimitiveType}}
{{^isPrimitiveType}}
{{{dataType}}}.validateJsonElement(element);
{{/isPrimitiveType}}
{{/isNumber}}
{{/items}}
}
{{/isArray}}
{{/hasVars}}
{{#hasVars}}
{{{.}}}.validateJsonElement(jsonElement);
return; // return earlier as at least one schema is valid with respect to the Json object
//validCount++;
return;
{{/hasVars}}
return;
} catch (Exception e) {
errorMessages.add(String.format("Deserialization for {{{dataType}}} failed with `%s`.", e.getMessage()));
// continue to the next one
}
{{/anyOf}}
if (validCount == 0) {
throw new IOException(String.format("The JSON string is invalid for {{classname}} with anyOf schemas: {{#anyOf}}{{{.}}}{{^-last}}, {{/-last}}{{/anyOf}}. JSON: %s", jsonElement.toString()));
}
{{/composedSchemas}}
throw new IOException(String.format("The JSON string is invalid for {{classname}} with anyOf schemas: {{#anyOf}}{{{.}}}{{^-last}}, {{/-last}}{{/anyOf}}. no class match the result, expected at least 1. Detailed failure message for anyOf schemas: %s. JSON: %s", errorMessages, jsonElement.toString()));
}
/**

View File

@ -254,7 +254,6 @@ public class {{classname}} extends AbstractOpenApiSchema{{#vendorExtensions.x-im
* {{#oneOf}}{{{.}}}{{^-last}}, {{/-last}}{{/oneOf}}
*
* It could be an instance of the 'oneOf' schemas.
* The oneOf child schemas may themselves be a composed schema (allOf, anyOf, oneOf).
*/
@Override
public void setActualInstance(Object instance) {

View File

@ -2308,6 +2308,13 @@ components:
maxLength: 1089
- type: number
- type: boolean
ScalarAnyOf:
description: Values of scalar type using anyOf
anyOf:
- type: string
maxLength: 1089
- type: number
- type: boolean
Array:
description: Values of array type
type: array

View File

@ -173,7 +173,6 @@ public class SimpleOneOf extends AbstractOpenApiSchema implements Serializable {
* Integer, String
*
* It could be an instance of the 'oneOf' schemas.
* The oneOf child schemas may themselves be a composed schema (allOf, anyOf, oneOf).
*/
@Override
public void setActualInstance(Object instance) {

View File

@ -80,6 +80,7 @@ docs/Quadrilateral.md
docs/QuadrilateralInterface.md
docs/ReadOnlyFirst.md
docs/Scalar.md
docs/ScalarAnyOf.md
docs/ScaleneTriangle.md
docs/Shape.md
docs/ShapeInterface.md
@ -206,6 +207,7 @@ src/main/java/org/openapitools/client/model/Quadrilateral.java
src/main/java/org/openapitools/client/model/QuadrilateralInterface.java
src/main/java/org/openapitools/client/model/ReadOnlyFirst.java
src/main/java/org/openapitools/client/model/Scalar.java
src/main/java/org/openapitools/client/model/ScalarAnyOf.java
src/main/java/org/openapitools/client/model/ScaleneTriangle.java
src/main/java/org/openapitools/client/model/Shape.java
src/main/java/org/openapitools/client/model/ShapeInterface.java

View File

@ -229,6 +229,7 @@ Class | Method | HTTP request | Description
- [QuadrilateralInterface](docs/QuadrilateralInterface.md)
- [ReadOnlyFirst](docs/ReadOnlyFirst.md)
- [Scalar](docs/Scalar.md)
- [ScalarAnyOf](docs/ScalarAnyOf.md)
- [ScaleneTriangle](docs/ScaleneTriangle.md)
- [Shape](docs/Shape.md)
- [ShapeInterface](docs/ShapeInterface.md)

View File

@ -2315,6 +2315,13 @@ components:
type: string
- type: number
- type: boolean
ScalarAnyOf:
anyOf:
- maxLength: 1089
type: string
- type: number
- type: boolean
description: Values of scalar type using anyOf
Array:
description: Values of array type
items:

View File

@ -0,0 +1,13 @@
# ScalarAnyOf
Values of scalar type using anyOf
## Properties
| Name | Type | Description | Notes |
|------------ | ------------- | ------------- | -------------|

View File

@ -292,6 +292,7 @@ public class JSON {
gsonBuilder.registerTypeAdapterFactory(new org.openapitools.client.model.QuadrilateralInterface.CustomTypeAdapterFactory());
gsonBuilder.registerTypeAdapterFactory(new org.openapitools.client.model.ReadOnlyFirst.CustomTypeAdapterFactory());
gsonBuilder.registerTypeAdapterFactory(new org.openapitools.client.model.Scalar.CustomTypeAdapterFactory());
gsonBuilder.registerTypeAdapterFactory(new org.openapitools.client.model.ScalarAnyOf.CustomTypeAdapterFactory());
gsonBuilder.registerTypeAdapterFactory(new org.openapitools.client.model.ScaleneTriangle.CustomTypeAdapterFactory());
gsonBuilder.registerTypeAdapterFactory(new org.openapitools.client.model.Shape.CustomTypeAdapterFactory());
gsonBuilder.registerTypeAdapterFactory(new org.openapitools.client.model.ShapeInterface.CustomTypeAdapterFactory());

View File

@ -177,7 +177,6 @@ public class Fruit extends AbstractOpenApiSchema {
* Apple, Banana
*
* It could be an instance of the 'oneOf' schemas.
* The oneOf child schemas may themselves be a composed schema (allOf, anyOf, oneOf).
*/
@Override
public void setActualInstance(Object instance) {

View File

@ -177,7 +177,6 @@ public class FruitReq extends AbstractOpenApiSchema {
* AppleReq, BananaReq
*
* It could be an instance of the 'oneOf' schemas.
* The oneOf child schemas may themselves be a composed schema (allOf, anyOf, oneOf).
*/
@Override
public void setActualInstance(Object instance) {

View File

@ -35,6 +35,7 @@ import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import com.google.gson.Gson;
@ -54,6 +55,7 @@ import com.google.gson.JsonSerializationContext;
import com.google.gson.JsonSerializer;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonArray;
import com.google.gson.JsonParseException;
import org.openapitools.client.JSON;
@ -83,19 +85,17 @@ public class GmFruit extends AbstractOpenApiSchema {
// check if the actual instance is of the type `Apple`
if (value.getActualInstance() instanceof Apple) {
JsonObject obj = adapterApple.toJsonTree((Apple)value.getActualInstance()).getAsJsonObject();
elementAdapter.write(out, obj);
return;
JsonElement element = adapterApple.toJsonTree((Apple)value.getActualInstance());
elementAdapter.write(out, element);
return;
}
// check if the actual instance is of the type `Banana`
if (value.getActualInstance() instanceof Banana) {
JsonObject obj = adapterBanana.toJsonTree((Banana)value.getActualInstance()).getAsJsonObject();
elementAdapter.write(out, obj);
return;
JsonElement element = adapterBanana.toJsonTree((Banana)value.getActualInstance());
elementAdapter.write(out, element);
return;
}
throw new IOException("Failed to serialize as the type doesn't match anyOf schemas: Apple, Banana");
throw new IOException("Failed to serialize as the type doesn't match anyOf schemae: Apple, Banana");
}
@Override
@ -103,34 +103,37 @@ public class GmFruit extends AbstractOpenApiSchema {
Object deserialized = null;
JsonElement jsonElement = elementAdapter.read(in);
ArrayList<String> errorMessages = new ArrayList<>();
TypeAdapter actualAdapter = elementAdapter;
// deserialize Apple
try {
// validate the JSON object to see if any exception is thrown
Apple.validateJsonElement(jsonElement);
log.log(Level.FINER, "Input data matches schema 'Apple'");
GmFruit ret = new GmFruit();
ret.setActualInstance(adapterApple.fromJsonTree(jsonElement));
return ret;
// validate the JSON object to see if any exception is thrown
Apple.validateJsonElement(jsonElement);
actualAdapter = adapterApple;
GmFruit ret = new GmFruit();
ret.setActualInstance(actualAdapter.fromJsonTree(jsonElement));
return ret;
} catch (Exception e) {
// deserialization failed, continue
log.log(Level.FINER, "Input data does not match schema 'Apple'", e);
// deserialization failed, continue
errorMessages.add(String.format("Deserialization for Apple failed with `%s`.", e.getMessage()));
log.log(Level.FINER, "Input data does not match schema 'Apple'", e);
}
// deserialize Banana
try {
// validate the JSON object to see if any exception is thrown
Banana.validateJsonElement(jsonElement);
log.log(Level.FINER, "Input data matches schema 'Banana'");
GmFruit ret = new GmFruit();
ret.setActualInstance(adapterBanana.fromJsonTree(jsonElement));
return ret;
// validate the JSON object to see if any exception is thrown
Banana.validateJsonElement(jsonElement);
actualAdapter = adapterBanana;
GmFruit ret = new GmFruit();
ret.setActualInstance(actualAdapter.fromJsonTree(jsonElement));
return ret;
} catch (Exception e) {
// deserialization failed, continue
log.log(Level.FINER, "Input data does not match schema 'Banana'", e);
// deserialization failed, continue
errorMessages.add(String.format("Deserialization for Banana failed with `%s`.", e.getMessage()));
log.log(Level.FINER, "Input data does not match schema 'Banana'", e);
}
throw new IOException(String.format("Failed deserialization for GmFruit: no class matched. JSON: %s", jsonElement.toString()));
throw new IOException(String.format("Failed deserialization for GmFruit: no class matches result, expected at least 1. Detailed failure message for anyOf schemas: %s. JSON: %s", errorMessages, jsonElement.toString()));
}
}.nullSafe();
}
@ -169,7 +172,6 @@ public class GmFruit extends AbstractOpenApiSchema {
* Apple, Banana
*
* It could be an instance of the 'anyOf' schemas.
* The anyOf child schemas may themselves be a composed schema (allOf, anyOf, anyOf).
*/
@Override
public void setActualInstance(Object instance) {
@ -207,7 +209,6 @@ public class GmFruit extends AbstractOpenApiSchema {
public Apple getApple() throws ClassCastException {
return (Apple)super.getActualInstance();
}
/**
* Get the actual instance of `Banana`. If the actual instance is not `Banana`,
* the ClassCastException will be thrown.
@ -219,7 +220,6 @@ public class GmFruit extends AbstractOpenApiSchema {
return (Banana)super.getActualInstance();
}
/**
* Validates the JSON Element and throws an exception if issues found
*
@ -228,26 +228,25 @@ public class GmFruit extends AbstractOpenApiSchema {
*/
public static void validateJsonElement(JsonElement jsonElement) throws IOException {
// validate anyOf schemas one by one
int validCount = 0;
ArrayList<String> errorMessages = new ArrayList<>();
// validate the json string with Apple
try {
Apple.validateJsonElement(jsonElement);
return; // return earlier as at least one schema is valid with respect to the Json object
//validCount++;
return;
} catch (Exception e) {
errorMessages.add(String.format("Deserialization for Apple failed with `%s`.", e.getMessage()));
// continue to the next one
}
// validate the json string with Banana
try {
Banana.validateJsonElement(jsonElement);
return; // return earlier as at least one schema is valid with respect to the Json object
//validCount++;
return;
} catch (Exception e) {
errorMessages.add(String.format("Deserialization for Banana failed with `%s`.", e.getMessage()));
// continue to the next one
}
if (validCount == 0) {
throw new IOException(String.format("The JSON string is invalid for GmFruit with anyOf schemas: Apple, Banana. JSON: %s", jsonElement.toString()));
}
throw new IOException(String.format("The JSON string is invalid for GmFruit with anyOf schemas: Apple, Banana. no class match the result, expected at least 1. Detailed failure message for anyOf schemas: %s. JSON: %s", errorMessages, jsonElement.toString()));
}
/**

View File

@ -228,7 +228,6 @@ public class Mammal extends AbstractOpenApiSchema {
* Pig, Whale, Zebra
*
* It could be an instance of the 'oneOf' schemas.
* The oneOf child schemas may themselves be a composed schema (allOf, anyOf, oneOf).
*/
@Override
public void setActualInstance(Object instance) {

View File

@ -198,7 +198,6 @@ public class NullableShape extends AbstractOpenApiSchema {
* Quadrilateral, Triangle
*
* It could be an instance of the 'oneOf' schemas.
* The oneOf child schemas may themselves be a composed schema (allOf, anyOf, oneOf).
*/
@Override
public void setActualInstance(Object instance) {

View File

@ -198,7 +198,6 @@ public class Pig extends AbstractOpenApiSchema {
* BasquePig, DanishPig
*
* It could be an instance of the 'oneOf' schemas.
* The oneOf child schemas may themselves be a composed schema (allOf, anyOf, oneOf).
*/
@Override
public void setActualInstance(Object instance) {

View File

@ -198,7 +198,6 @@ public class Quadrilateral extends AbstractOpenApiSchema {
* ComplexQuadrilateral, SimpleQuadrilateral
*
* It could be an instance of the 'oneOf' schemas.
* The oneOf child schemas may themselves be a composed schema (allOf, anyOf, oneOf).
*/
@Override
public void setActualInstance(Object instance) {

View File

@ -200,7 +200,6 @@ public class Scalar extends AbstractOpenApiSchema {
* BigDecimal, Boolean, String
*
* It could be an instance of the 'oneOf' schemas.
* The oneOf child schemas may themselves be a composed schema (allOf, anyOf, oneOf).
*/
@Override
public void setActualInstance(Object instance) {

View File

@ -0,0 +1,325 @@
/*
* OpenAPI Petstore
* This spec is mainly for testing Petstore server and contains fake endpoints, models. Please do not use this for any other purpose. Special characters: \" \\
*
* The version of the OpenAPI document: 1.0.0
*
*
* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
* https://openapi-generator.tech
* Do not edit the class manually.
*/
package org.openapitools.client.model;
import java.util.Objects;
import java.util.Arrays;
import java.math.BigDecimal;
import java.io.IOException;
import java.lang.reflect.Type;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonParseException;
import com.google.gson.TypeAdapter;
import com.google.gson.TypeAdapterFactory;
import com.google.gson.reflect.TypeToken;
import com.google.gson.JsonPrimitive;
import com.google.gson.annotations.JsonAdapter;
import com.google.gson.annotations.SerializedName;
import com.google.gson.stream.JsonReader;
import com.google.gson.stream.JsonWriter;
import com.google.gson.JsonDeserializationContext;
import com.google.gson.JsonDeserializer;
import com.google.gson.JsonSerializationContext;
import com.google.gson.JsonSerializer;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonArray;
import com.google.gson.JsonParseException;
import org.openapitools.client.JSON;
@javax.annotation.Generated(value = "org.openapitools.codegen.languages.JavaClientCodegen")
public class ScalarAnyOf extends AbstractOpenApiSchema {
private static final Logger log = Logger.getLogger(ScalarAnyOf.class.getName());
public static class CustomTypeAdapterFactory implements TypeAdapterFactory {
@SuppressWarnings("unchecked")
@Override
public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> type) {
if (!ScalarAnyOf.class.isAssignableFrom(type.getRawType())) {
return null; // this class only serializes 'ScalarAnyOf' and its subtypes
}
final TypeAdapter<JsonElement> elementAdapter = gson.getAdapter(JsonElement.class);
final TypeAdapter<String> adapterString = gson.getDelegateAdapter(this, TypeToken.get(String.class));
final TypeAdapter<BigDecimal> adapterBigDecimal = gson.getDelegateAdapter(this, TypeToken.get(BigDecimal.class));
final TypeAdapter<Boolean> adapterBoolean = gson.getDelegateAdapter(this, TypeToken.get(Boolean.class));
return (TypeAdapter<T>) new TypeAdapter<ScalarAnyOf>() {
@Override
public void write(JsonWriter out, ScalarAnyOf value) throws IOException {
if (value == null || value.getActualInstance() == null) {
elementAdapter.write(out, null);
return;
}
// check if the actual instance is of the type `String`
if (value.getActualInstance() instanceof String) {
JsonPrimitive primitive = adapterString.toJsonTree((String)value.getActualInstance()).getAsJsonPrimitive();
elementAdapter.write(out, primitive);
return;
}
// check if the actual instance is of the type `BigDecimal`
if (value.getActualInstance() instanceof BigDecimal) {
JsonElement element = adapterBigDecimal.toJsonTree((BigDecimal)value.getActualInstance());
elementAdapter.write(out, element);
return;
}
// check if the actual instance is of the type `Boolean`
if (value.getActualInstance() instanceof Boolean) {
JsonPrimitive primitive = adapterBoolean.toJsonTree((Boolean)value.getActualInstance()).getAsJsonPrimitive();
elementAdapter.write(out, primitive);
return;
}
throw new IOException("Failed to serialize as the type doesn't match anyOf schemae: BigDecimal, Boolean, String");
}
@Override
public ScalarAnyOf read(JsonReader in) throws IOException {
Object deserialized = null;
JsonElement jsonElement = elementAdapter.read(in);
ArrayList<String> errorMessages = new ArrayList<>();
TypeAdapter actualAdapter = elementAdapter;
// deserialize String
try {
// validate the JSON object to see if any exception is thrown
if(!jsonElement.getAsJsonPrimitive().isString()) {
throw new IllegalArgumentException(String.format("Expected json element to be of type String in the JSON string but got `%s`", jsonElement.toString()));
}
actualAdapter = adapterString;
ScalarAnyOf ret = new ScalarAnyOf();
ret.setActualInstance(actualAdapter.fromJsonTree(jsonElement));
return ret;
} catch (Exception e) {
// deserialization failed, continue
errorMessages.add(String.format("Deserialization for String failed with `%s`.", e.getMessage()));
log.log(Level.FINER, "Input data does not match schema 'String'", e);
}
// deserialize BigDecimal
try {
// validate the JSON object to see if any exception is thrown
if(!jsonElement.getAsJsonPrimitive().isNumber()) {
throw new IllegalArgumentException(String.format("Expected json element to be of type Number in the JSON string but got `%s`", jsonElement.toString()));
}
actualAdapter = adapterBigDecimal;
ScalarAnyOf ret = new ScalarAnyOf();
ret.setActualInstance(actualAdapter.fromJsonTree(jsonElement));
return ret;
} catch (Exception e) {
// deserialization failed, continue
errorMessages.add(String.format("Deserialization for BigDecimal failed with `%s`.", e.getMessage()));
log.log(Level.FINER, "Input data does not match schema 'BigDecimal'", e);
}
// deserialize Boolean
try {
// validate the JSON object to see if any exception is thrown
if(!jsonElement.getAsJsonPrimitive().isBoolean()) {
throw new IllegalArgumentException(String.format("Expected json element to be of type Boolean in the JSON string but got `%s`", jsonElement.toString()));
}
actualAdapter = adapterBoolean;
ScalarAnyOf ret = new ScalarAnyOf();
ret.setActualInstance(actualAdapter.fromJsonTree(jsonElement));
return ret;
} catch (Exception e) {
// deserialization failed, continue
errorMessages.add(String.format("Deserialization for Boolean failed with `%s`.", e.getMessage()));
log.log(Level.FINER, "Input data does not match schema 'Boolean'", e);
}
throw new IOException(String.format("Failed deserialization for ScalarAnyOf: no class matches result, expected at least 1. Detailed failure message for anyOf schemas: %s. JSON: %s", errorMessages, jsonElement.toString()));
}
}.nullSafe();
}
}
// store a list of schema names defined in anyOf
public static final Map<String, Class<?>> schemas = new HashMap<String, Class<?>>();
public ScalarAnyOf() {
super("anyOf", Boolean.FALSE);
}
public ScalarAnyOf(BigDecimal o) {
super("anyOf", Boolean.FALSE);
setActualInstance(o);
}
public ScalarAnyOf(Boolean o) {
super("anyOf", Boolean.FALSE);
setActualInstance(o);
}
public ScalarAnyOf(String o) {
super("anyOf", Boolean.FALSE);
setActualInstance(o);
}
static {
schemas.put("String", String.class);
schemas.put("BigDecimal", BigDecimal.class);
schemas.put("Boolean", Boolean.class);
}
@Override
public Map<String, Class<?>> getSchemas() {
return ScalarAnyOf.schemas;
}
/**
* Set the instance that matches the anyOf child schema, check
* the instance parameter is valid against the anyOf child schemas:
* BigDecimal, Boolean, String
*
* It could be an instance of the 'anyOf' schemas.
*/
@Override
public void setActualInstance(Object instance) {
if (instance instanceof String) {
super.setActualInstance(instance);
return;
}
if (instance instanceof BigDecimal) {
super.setActualInstance(instance);
return;
}
if (instance instanceof Boolean) {
super.setActualInstance(instance);
return;
}
throw new RuntimeException("Invalid instance type. Must be BigDecimal, Boolean, String");
}
/**
* Get the actual instance, which can be the following:
* BigDecimal, Boolean, String
*
* @return The actual instance (BigDecimal, Boolean, String)
*/
@Override
public Object getActualInstance() {
return super.getActualInstance();
}
/**
* Get the actual instance of `String`. If the actual instance is not `String`,
* the ClassCastException will be thrown.
*
* @return The actual instance of `String`
* @throws ClassCastException if the instance is not `String`
*/
public String getString() throws ClassCastException {
return (String)super.getActualInstance();
}
/**
* Get the actual instance of `BigDecimal`. If the actual instance is not `BigDecimal`,
* the ClassCastException will be thrown.
*
* @return The actual instance of `BigDecimal`
* @throws ClassCastException if the instance is not `BigDecimal`
*/
public BigDecimal getBigDecimal() throws ClassCastException {
return (BigDecimal)super.getActualInstance();
}
/**
* Get the actual instance of `Boolean`. If the actual instance is not `Boolean`,
* the ClassCastException will be thrown.
*
* @return The actual instance of `Boolean`
* @throws ClassCastException if the instance is not `Boolean`
*/
public Boolean getBoolean() throws ClassCastException {
return (Boolean)super.getActualInstance();
}
/**
* Validates the JSON Element and throws an exception if issues found
*
* @param jsonElement JSON Element
* @throws IOException if the JSON Element is invalid with respect to ScalarAnyOf
*/
public static void validateJsonElement(JsonElement jsonElement) throws IOException {
// validate anyOf schemas one by one
ArrayList<String> errorMessages = new ArrayList<>();
// validate the json string with String
try {
if(!jsonElement.getAsJsonPrimitive().isString()) {
throw new IllegalArgumentException(String.format("Expected json element to be of type String in the JSON string but got `%s`", jsonElement.toString()));
}
return;
} catch (Exception e) {
errorMessages.add(String.format("Deserialization for String failed with `%s`.", e.getMessage()));
// continue to the next one
}
// validate the json string with BigDecimal
try {
if(!jsonElement.getAsJsonPrimitive().isNumber()) {
throw new IllegalArgumentException(String.format("Expected json element to be of type Number in the JSON string but got `%s`", jsonElement.toString()));
}
return;
} catch (Exception e) {
errorMessages.add(String.format("Deserialization for BigDecimal failed with `%s`.", e.getMessage()));
// continue to the next one
}
// validate the json string with Boolean
try {
if(!jsonElement.getAsJsonPrimitive().isBoolean()) {
throw new IllegalArgumentException(String.format("Expected json element to be of type Boolean in the JSON string but got `%s`", jsonElement.toString()));
}
return;
} catch (Exception e) {
errorMessages.add(String.format("Deserialization for Boolean failed with `%s`.", e.getMessage()));
// continue to the next one
}
throw new IOException(String.format("The JSON string is invalid for ScalarAnyOf with anyOf schemas: BigDecimal, Boolean, String. no class match the result, expected at least 1. Detailed failure message for anyOf schemas: %s. JSON: %s", errorMessages, jsonElement.toString()));
}
/**
* Create an instance of ScalarAnyOf given an JSON string
*
* @param jsonString JSON string
* @return An instance of ScalarAnyOf
* @throws IOException if the JSON string is invalid with respect to ScalarAnyOf
*/
public static ScalarAnyOf fromJson(String jsonString) throws IOException {
return JSON.getGson().fromJson(jsonString, ScalarAnyOf.class);
}
/**
* Convert an instance of ScalarAnyOf to an JSON string
*
* @return JSON string
*/
public String toJson() {
return JSON.getGson().toJson(this);
}
}

View File

@ -198,7 +198,6 @@ public class Shape extends AbstractOpenApiSchema {
* Quadrilateral, Triangle
*
* It could be an instance of the 'oneOf' schemas.
* The oneOf child schemas may themselves be a composed schema (allOf, anyOf, oneOf).
*/
@Override
public void setActualInstance(Object instance) {

View File

@ -198,7 +198,6 @@ public class ShapeOrNull extends AbstractOpenApiSchema {
* Quadrilateral, Triangle
*
* It could be an instance of the 'oneOf' schemas.
* The oneOf child schemas may themselves be a composed schema (allOf, anyOf, oneOf).
*/
@Override
public void setActualInstance(Object instance) {

View File

@ -228,7 +228,6 @@ public class Triangle extends AbstractOpenApiSchema {
* EquilateralTriangle, IsoscelesTriangle, ScaleneTriangle
*
* It could be an instance of the 'oneOf' schemas.
* The oneOf child schemas may themselves be a composed schema (allOf, anyOf, oneOf).
*/
@Override
public void setActualInstance(Object instance) {

View File

@ -182,7 +182,6 @@ public class Value extends AbstractOpenApiSchema {
* List<Scalar>, Scalar
*
* It could be an instance of the 'oneOf' schemas.
* The oneOf child schemas may themselves be a composed schema (allOf, anyOf, oneOf).
*/
@Override
public void setActualInstance(Object instance) {

View File

@ -740,4 +740,39 @@ public class JSONTest {
p.setTypeWithUnderscore("test3");
assertEquals(json.getGson().toJson(p), "{\"_type\":\"test1\",\"type\":\"test2\",\"type_\":\"test3\"}");
}
/**
* Validate a anyOf schema can be deserialized into the expected class.
* The anyOf schema contains primitive Types.
*/
@Test
public void testAnyOfSchemaWithPrimitives() throws Exception {
{
// string test
String str = "\"just a string\"";
ScalarAnyOf s = json.getGson().fromJson(str, ScalarAnyOf.class);
assertTrue(s.getActualInstance() instanceof String);
assertEquals(json.getGson().toJson(s), str);
}
{
// number test
String str = "123.45";
ScalarAnyOf s = json.getGson().fromJson(str, ScalarAnyOf.class);
assertTrue(s.getActualInstance() instanceof BigDecimal);
assertEquals(json.getGson().toJson(s), str);
}
{
// boolean test
String str = "true";
ScalarAnyOf s = json.getGson().fromJson(str, ScalarAnyOf.class);
assertTrue(s.getActualInstance() instanceof Boolean);
assertEquals(json.getGson().toJson(s), str);
}
// test no match
com.google.gson.JsonSyntaxException thrown = Assertions.assertThrows(com.google.gson.JsonSyntaxException.class, () -> {
String str = "{\"id\": 5847, \"name\":\"tag test 1\"}";
ScalarAnyOf s = json.getGson().fromJson(str, ScalarAnyOf.class);
});
}
}

View File

@ -0,0 +1,34 @@
/*
* OpenAPI Petstore
* This spec is mainly for testing Petstore server and contains fake endpoints, models. Please do not use this for any other purpose. Special characters: \" \\
*
* The version of the OpenAPI document: 1.0.0
*
*
* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
* https://openapi-generator.tech
* Do not edit the class manually.
*/
package org.openapitools.client.model;
import java.math.BigDecimal;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
/**
* Model tests for ScalarAnyOf
*/
public class ScalarAnyOfTest {
private final ScalarAnyOf model = new ScalarAnyOf();
/**
* Model tests for ScalarAnyOf
*/
@Test
public void testScalarAnyOf() {
// TODO: test ScalarAnyOf
}
}