org.javers.core.json.JsonConverterBuilder Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of javers-core Show documentation
Show all versions of javers-core Show documentation
JaVers - object auditing and diff framework for Java
package org.javers.core.json;
import com.google.gson.*;
import org.javers.common.validation.Validate;
import org.javers.core.json.typeadapter.util.UtilTypeCoreAdapters;
import org.javers.core.metamodel.annotation.DiffIgnore;
import org.javers.java8support.Java8TypeAdapters;
import org.javers.java8support.OptionalTypeAdapter;
import java.lang.reflect.Type;
import java.util.Collection;
import java.util.List;
/**
* @author bartosz walacik
* @see JsonConverter
*/
public class JsonConverterBuilder {
private static final String ISO_DATE_TIME_FORMAT = "yyyy-MM-dd'T'HH:mm:ssZ";
private boolean typeSafeValues = false;
private boolean prettyPrint;
private final GsonBuilder gsonBuilder;
public JsonConverterBuilder() {
this.gsonBuilder = new GsonBuilder();
this.gsonBuilder.setExclusionStrategies(new SkipFieldExclusionStrategy());
this.gsonBuilder.setObjectToNumberStrategy(ToNumberPolicy.LONG_OR_DOUBLE);
registerBuiltInAdapters(Java8TypeAdapters.adapters());
registerBuiltInAdapters(UtilTypeCoreAdapters.adapters());
registerJsonAdvancedTypeAdapter(new OptionalTypeAdapter());
}
/**
* When switched to true, all {@link org.javers.core.diff.changetype.Atomic}s
* are serialized type safely as a type + value pair, for example:
*
* {
* "typeAlias": "LocalDate"
* "value": "2001-01-01"
* }
*
* TypeAlias is defaulted to value.class.simpleName.
*
*
* Useful when serializing polymorfic collections like List or List<Object>
*
* @param typeSafeValues default false
*/
public JsonConverterBuilder typeSafeValues(boolean typeSafeValues) {
this.typeSafeValues = typeSafeValues;
return this;
}
/**
* @param prettyPrint default true
*/
public JsonConverterBuilder prettyPrint(boolean prettyPrint) {
this.prettyPrint = prettyPrint;
return this;
}
/**
* @param nativeAdapter should be null safe, if not so,
* simply call {@link TypeAdapter#nullSafe()} before registering it
* @see TypeAdapter
*/
public JsonConverterBuilder registerNativeTypeAdapter(Type targetType, TypeAdapter nativeAdapter) {
Validate.argumentsAreNotNull(targetType, nativeAdapter);
gsonBuilder.registerTypeAdapter(targetType, nativeAdapter);
return this;
}
/**
* @see JsonSerializer
*/
public JsonConverterBuilder registerNativeGsonSerializer(Type targetType, JsonSerializer jsonSerializer) {
Validate.argumentsAreNotNull(targetType, jsonSerializer);
gsonBuilder.registerTypeAdapter(targetType, jsonSerializer);
return this;
}
/**
* @since 3.1
* @see JsonSerializer
*/
public JsonConverterBuilder registerNativeGsonHierarchySerializer(Class targetType, JsonSerializer jsonSerializer) {
Validate.argumentsAreNotNull(targetType, jsonSerializer);
gsonBuilder.registerTypeHierarchyAdapter(targetType, jsonSerializer);
return this;
}
/**
* @since 3.1
* @see JsonDeserializer
*/
public JsonConverterBuilder registerNativeGsonHierarchyDeserializer(Class targetType, JsonDeserializer jsonDeserializer) {
Validate.argumentsAreNotNull(targetType, jsonDeserializer);
gsonBuilder.registerTypeHierarchyAdapter(targetType, jsonDeserializer);
return this;
}
/**
* @see JsonDeserializer
*/
public JsonConverterBuilder registerNativeGsonDeserializer(Type targetType, JsonDeserializer jsonDeserializer) {
Validate.argumentsAreNotNull(targetType, jsonDeserializer);
gsonBuilder.registerTypeAdapter(targetType, jsonDeserializer);
return this;
}
public JsonConverterBuilder registerJsonTypeAdapters(Collection adapters) {
Validate.argumentIsNotNull(adapters);
for (JsonTypeAdapter adapter : adapters) {
registerJsonTypeAdapter(adapter);
}
return this;
}
/**
* Maps given {@link JsonTypeAdapter}
* into pair of {@link JsonDeserializer} and {@link JsonDeserializer}
* and registers them with this.gsonBuilder
*/
public JsonConverterBuilder registerJsonTypeAdapter(JsonTypeAdapter adapter) {
Validate.argumentIsNotNull(adapter);
adapter.getValueTypes().forEach( c -> registerJsonTypeAdapterForType((Class)c, adapter));
return this;
}
/**
* @since 3.1
*/
public JsonConverterBuilder registerJsonAdvancedTypeAdapter(JsonAdvancedTypeAdapter adapter) {
Validate.argumentIsNotNull(adapter);
JsonSerializer jsonSerializer = (value, type, jsonSerializationContext) -> adapter.toJson(value, type, jsonSerializationContext);
JsonDeserializer jsonDeserializer = (jsonElement, type, jsonDeserializationContext) -> adapter.fromJson(jsonElement, type, jsonDeserializationContext);
registerNativeGsonHierarchySerializer(adapter.getTypeSuperclass(), jsonSerializer);
registerNativeGsonHierarchyDeserializer(adapter.getTypeSuperclass(), jsonDeserializer);
return this;
}
private void registerJsonTypeAdapterForType(Class targetType, final JsonTypeAdapter adapter) {
JsonSerializer jsonSerializer = (value, type, jsonSerializationContext) -> adapter.toJson(value, jsonSerializationContext);
JsonDeserializer jsonDeserializer = (jsonElement, type, jsonDeserializationContext) -> adapter.fromJson(jsonElement, jsonDeserializationContext);
registerNativeGsonSerializer(targetType, jsonSerializer);
registerNativeGsonDeserializer(targetType, jsonDeserializer);
}
public JsonConverter build() {
registerBuiltInAdapter(new AtomicTypeAdapter(typeSafeValues));
if (prettyPrint){
gsonBuilder.setPrettyPrinting();
}
gsonBuilder.enableComplexMapKeySerialization();
gsonBuilder.serializeNulls()
.setDateFormat(ISO_DATE_TIME_FORMAT);
return new JsonConverter(gsonBuilder.create());
}
private void registerBuiltInAdapters(final List adapters) {
adapters.forEach(a -> {
if (a instanceof JsonAdvancedTypeAdapter) {
registerJsonAdvancedTypeAdapter((JsonAdvancedTypeAdapter) a);
}
if (a instanceof JsonTypeAdapter) {
registerBuiltInAdapter((JsonTypeAdapter) a);
}
});
}
private void registerBuiltInAdapter(final JsonTypeAdapter adapter) {
adapter.getValueTypes().forEach(c -> registerJsonTypeAdapterForType((Class) c, adapter));
}
private static class SkipFieldExclusionStrategy implements ExclusionStrategy {
public boolean shouldSkipClass(Class clazz) {
return clazz.getAnnotation(DiffIgnore.class) != null;
}
public boolean shouldSkipField(FieldAttributes field) {
return field.getAnnotation(DiffIgnore.class) != null;
}
}
}