Java.libraries.jersey3.JSON.mustache Maven / Gradle / Ivy
package {{invokerPackage}};
import com.fasterxml.jackson.annotation.*;
import com.fasterxml.jackson.databind.*;
import com.fasterxml.jackson.databind.json.JsonMapper;
{{#openApiNullable}}
import org.openapitools.jackson.nullable.JsonNullableModule;
{{/openApiNullable}}
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
{{#joda}}
import com.fasterxml.jackson.datatype.joda.JodaModule;
{{/joda}}
{{#models.0}}
import {{modelPackage}}.*;
{{/models.0}}
import java.text.DateFormat;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import {{javaxPackage}}.ws.rs.core.GenericType;
import {{javaxPackage}}.ws.rs.ext.ContextResolver;
{{>generatedAnnotation}}
public class JSON implements ContextResolver {
private ObjectMapper mapper;
public JSON() {
mapper = JsonMapper.builder()
.serializationInclusion(JsonInclude.Include.NON_NULL)
.configure(MapperFeature.ALLOW_COERCION_OF_SCALARS, false)
.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, true)
.configure(DeserializationFeature.FAIL_ON_INVALID_SUBTYPE, true)
.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS)
.enable(SerializationFeature.WRITE_ENUMS_USING_TO_STRING)
.enable(DeserializationFeature.READ_ENUMS_USING_TO_STRING)
.defaultDateFormat(new RFC3339DateFormat())
.addModule(new JavaTimeModule())
{{#joda}}
.addModule(new JodaModule())
{{/joda}}
{{#openApiNullable}}
.addModule(new JsonNullableModule())
{{/openApiNullable}}
.build();
}
/**
* Set the date format for JSON (de)serialization with Date properties.
* @param dateFormat Date format
*/
public void setDateFormat(DateFormat dateFormat) {
mapper.setDateFormat(dateFormat);
}
@Override
public ObjectMapper getContext(Class> type) {
return mapper;
}
/**
* Get the object mapper
*
* @return object mapper
*/
public ObjectMapper getMapper() { return mapper; }
/**
* Returns the target model class that should be used to deserialize the input data.
* The discriminator mappings are used to determine the target model class.
*
* @param node The input data.
* @param modelClass The class that contains the discriminator mappings.
*/
public static Class> getClassForElement(JsonNode node, Class> modelClass) {
ClassDiscriminatorMapping cdm = modelDiscriminators.get(modelClass);
if (cdm != null) {
return cdm.getClassForElement(node, new HashSet<>());
}
return null;
}
/**
* Helper class to register the discriminator mappings.
*/
private static class ClassDiscriminatorMapping {
// The model class name.
Class> modelClass;
// The name of the discriminator property.
String discriminatorName;
// The discriminator mappings for a model class.
Map> discriminatorMappings;
// Constructs a new class discriminator.
ClassDiscriminatorMapping(Class> cls, String propertyName, Map> mappings) {
modelClass = cls;
discriminatorName = propertyName;
discriminatorMappings = new HashMap<>();
if (mappings != null) {
discriminatorMappings.putAll(mappings);
}
}
// Return the name of the discriminator property for this model class.
String getDiscriminatorPropertyName() {
return discriminatorName;
}
// Return the discriminator value or null if the discriminator is not
// present in the payload.
String getDiscriminatorValue(JsonNode node) {
// Determine the value of the discriminator property in the input data.
if (discriminatorName != null) {
// Get the value of the discriminator property, if present in the input payload.
node = node.get(discriminatorName);
if (node != null && node.isValueNode()) {
String discrValue = node.asText();
if (discrValue != null) {
return discrValue;
}
}
}
return null;
}
/**
* Returns the target model class that should be used to deserialize the input data.
* This function can be invoked for anyOf/oneOf composed models with discriminator mappings.
* The discriminator mappings are used to determine the target model class.
*
* @param node The input data.
* @param visitedClasses The set of classes that have already been visited.
*/
Class> getClassForElement(JsonNode node, Set> visitedClasses) {
if (visitedClasses.contains(modelClass)) {
// Class has already been visited.
return null;
}
// Determine the value of the discriminator property in the input data.
String discrValue = getDiscriminatorValue(node);
if (discrValue == null) {
return null;
}
Class> cls = discriminatorMappings.get(discrValue);
// It may not be sufficient to return this cls directly because that target class
// may itself be a composed schema, possibly with its own discriminator.
visitedClasses.add(modelClass);
for (Class> childClass : discriminatorMappings.values()) {
ClassDiscriminatorMapping childCdm = modelDiscriminators.get(childClass);
if (childCdm == null) {
continue;
}
if (!discriminatorName.equals(childCdm.discriminatorName)) {
discrValue = getDiscriminatorValue(node);
if (discrValue == null) {
continue;
}
}
if (childCdm != null) {
// Recursively traverse the discriminator mappings.
Class> childDiscr = childCdm.getClassForElement(node, visitedClasses);
if (childDiscr != null) {
return childDiscr;
}
}
}
return cls;
}
}
/**
* Returns true if inst is an instance of modelClass in the OpenAPI model hierarchy.
*
* The Java class hierarchy is not implemented the same way as the OpenAPI model hierarchy,
* so it's not possible to use the instanceof keyword.
*
* @param modelClass A OpenAPI model class.
* @param inst The instance object.
*/
public static boolean isInstanceOf(Class> modelClass, Object inst, Set> visitedClasses) {
if (modelClass.isInstance(inst)) {
// This handles the 'allOf' use case with single parent inheritance.
return true;
}
if (visitedClasses.contains(modelClass)) {
// This is to prevent infinite recursion when the composed schemas have
// a circular dependency.
return false;
}
visitedClasses.add(modelClass);
// Traverse the oneOf/anyOf composed schemas.
Map descendants = modelDescendants.get(modelClass);
if (descendants != null) {
for (GenericType childType : descendants.values()) {
if (isInstanceOf(childType.getRawType(), inst, visitedClasses)) {
return true;
}
}
}
return false;
}
/**
* A map of discriminators for all model classes.
*/
private static Map, ClassDiscriminatorMapping> modelDiscriminators = new HashMap<>();
/**
* A map of oneOf/anyOf descendants for each model class.
*/
private static Map, Map> modelDescendants = new HashMap<>();
/**
* Register a model class discriminator.
*
* @param modelClass the model class
* @param discriminatorPropertyName the name of the discriminator property
* @param mappings a map with the discriminator mappings.
*/
public static void registerDiscriminator(Class> modelClass, String discriminatorPropertyName, Map> mappings) {
ClassDiscriminatorMapping m = new ClassDiscriminatorMapping(modelClass, discriminatorPropertyName, mappings);
modelDiscriminators.put(modelClass, m);
}
/**
* Register the oneOf/anyOf descendants of the modelClass.
*
* @param modelClass the model class
* @param descendants a map of oneOf/anyOf descendants.
*/
public static void registerDescendants(Class> modelClass, Map descendants) {
modelDescendants.put(modelClass, descendants);
}
private static JSON json;
static
{
json = new JSON();
}
/**
* Get the default JSON instance.
*
* @return the default JSON instance
*/
public static JSON getDefault() {
return json;
}
/**
* Set the default JSON instance.
*
* @param json JSON instance to be used
*/
public static void setDefault(JSON json) {
JSON.json = json;
}
}