ee.telekom.workflow.util.JsonUtil Maven / Gradle / Ivy
package ee.telekom.workflow.util;
import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonNull;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import com.google.gson.JsonPrimitive;
import com.google.gson.internal.LinkedTreeMap;
/**
* Utility that serialises and deserialises any deep object tree without cyclic references.
*
* Common JSON libraries such as jackson or gson all support to serialise such deep object trees.
* However, none of the known libraries supports to to deserialise json strings that describe
* Object[] or Map fields or classes that contain fields of collections.
*
* The general problem is that the class information of a given object instance is lost during
* serialisation. E.g. it is not possible to deserialise the object array
*
new SportMatch[] {new FootballMatch("Arsenal","Chelsea"), new TennisMatch("Nadal","Djokovic")}
* based on the json representation
* [{oponent1:"Arsenal", oponent2:"Chelsea"},{oponent1:"Nadal", oponent2:"Djokovic"}]
*
* This library overcomes this issue and also serialises class information where the class type
* cannot be deducted otherwise. When serialising it allows to choose whether to serialise class
* type information for the root object. Not serialising root object class type information is
* appropriate if you can predict the root object class type at deserialization time. E.g.
*
* // if you can predict the type
* String json = JsonUtil.serialize(new SportMatch[] {new FootballMatch("Arsenal","Chelsea"), new TennisMatch("Nadal","Djokovic")},false);
* SportMatch[] o = JsonUtil.deserialize(json, SportMatch[].class);
*
* // if you cannot predict the type
* String json = JsonUtil.serialize(new SportMatch[] {new FootballMatch("Arsenal","Chelsea"), new TennisMatch("Nadal","Djokovic")},true);
* Object o = JsonUtil.deserialize(json);
*
*/
public class JsonUtil{
private static final Gson gson = new GsonBuilder().serializeNulls().create();
private static final String ARRAYS_ASLIST_CLASSNAME = Arrays.asList( "" ).getClass().getName();
private static final String SINGLETON_LIST_CLASSNAME = Collections.singletonList( "" ).getClass().getName();
private static final String SINGLETON_SET_CLASSNAME = Collections.singleton( "" ).getClass().getName();
private static final String SINGLETON_MAP_CLASSNAME = Collections.singletonMap( "", "" ).getClass().getName();
private static final Class> ARRAYS_ASLIST_CLASS = Arrays.asList( "" ).getClass();
private static final Class> SINGLETON_LIST_CLASS = Collections.singletonList( "" ).getClass();
private static final Class> SINGLETON_SET_CLASS = Collections.singleton( "" ).getClass();
private static final Class> SINGLETON_MAP_CLASS = Collections.singletonMap( "", "" ).getClass();
public static String serialize( boolean object ){
return gson.toJson( convert( object, boolean.class, false ) );
}
public static String serialize( byte object ){
return gson.toJson( convert( object, byte.class, false ) );
}
public static String serialize( short object ){
return gson.toJson( convert( object, short.class, false ) );
}
public static String serialize( int object ){
return gson.toJson( convert( object, int.class, false ) );
}
public static String serialize( long object ){
return gson.toJson( convert( object, long.class, false ) );
}
public static String serialize( float object ){
return gson.toJson( convert( object, float.class, false ) );
}
public static String serialize( double object ){
return gson.toJson( convert( object, double.class, false ) );
}
public static String serialize( Object object, boolean serializeType ){
if( object == null ){
return null;
}
Class> type = object.getClass();
return gson.toJson( convert( object, type, serializeType ) );
}
public static String serializeCollection( Collection> object, boolean serializeType, boolean serializeElementType ){
if( object == null ){
return null;
}
Class> type = object.getClass();
return gson.toJson( convertCollection( object, type, serializeType, serializeElementType ) );
}
public static boolean deserializeBoolean( String json ){
return gson.fromJson( json, boolean.class );
}
public static byte deserializeByte( String json ){
return gson.fromJson( json, byte.class );
}
public static short deserializeShort( String json ){
return gson.fromJson( json, short.class );
}
public static int deserializeInt( String json ){
return gson.fromJson( json, int.class );
}
public static long deserializeLong( String json ){
return gson.fromJson( json, long.class );
}
public static float deserializeFloat( String json ){
return gson.fromJson( json, float.class );
}
public static double deserializeDouble( String json ){
return gson.fromJson( json, double.class );
}
public static Object deserialize( String json ){
if( json == null ){
return null;
}
return deserialize( json, null );
}
public static T deserialize( String json, Class type ){
if( json == null ){
return null;
}
JsonParser parser = new JsonParser();
JsonElement element = parser.parse( json );
@SuppressWarnings("unchecked")
T result = (T)deconvert( element, type );
return result;
}
public static HashMap deserializeHashMap( String json, Class key, Class value ){
@SuppressWarnings("unchecked")
HashMap result = deserialize( json, HashMap.class );
return result;
}
public static Collection deserializeCollection( String json, @SuppressWarnings("rawtypes") Class extends Collection> type, Class elementType ){
if( json == null ){
return null;
}
JsonParser parser = new JsonParser();
JsonElement element = parser.parse( json );
@SuppressWarnings("unchecked")
Collection result = (Collection)deconvertCollection( element, type, elementType );
return result;
}
private static JsonElement convert( Object object, Class> type, boolean serializeType ){
if( object == null ){
return JsonNull.INSTANCE;
}
else if( isSimple( object.getClass() ) ){
return convertSimple( object, type, serializeType );
}
else if( isArray( object.getClass() ) ){
return convertArray( object, type, serializeType );
}
else if( isCollection( object.getClass() ) ){
return convertCollection( object, type, serializeType, true );
}
else if( isMap( object.getClass() ) ){
return convertMap( object, type, serializeType );
}
else{
return convertObject( object, type, serializeType );
}
}
private static Object deconvert( JsonElement element, Class> expectedType ){
if( element.isJsonNull() ){
return null;
}
else if( element.isJsonPrimitive() ){
return deconvertSimple( element, expectedType );
}
Class> objectType;
JsonElement objectElement;
if( expectedType == null || isContainer( element ) ){
JsonObject container = element.getAsJsonObject();
String typeName = container.get( "c" ).getAsString();
objectType = getClass( typeName );
objectElement = container.get( "v" );
}
else{
objectType = expectedType;
objectElement = element;
}
if( isSimple( objectType ) ){
return deconvertSimple( objectElement, objectType );
}
else if( isArray( objectType ) ){
return deconvertArray( objectElement, objectType );
}
else if( isCollection( objectType ) ){
return deconvertCollection( objectElement, objectType, null );
}
else if( isMap( objectType ) ){
return deconvertMap( objectElement, objectType );
}
else{
return deconvertObject( objectElement, objectType );
}
}
private static boolean isSimple( Class> type ){
return Boolean.class.isAssignableFrom( type )
|| Number.class.isAssignableFrom( type )
|| String.class.isAssignableFrom( type )
|| Date.class.isAssignableFrom( type ) || type.isEnum();
}
private static JsonElement convertSimple( Object object, Class> type, boolean serializeType ){
JsonPrimitive primitive = null;
if( object instanceof Boolean ){
primitive = new JsonPrimitive( (Boolean)object );
}
else if( object instanceof Number ){
primitive = new JsonPrimitive( (Number)object );
}
else if( object instanceof String ){
primitive = new JsonPrimitive( (String)object );
}
else if( object instanceof Date ){
primitive = new JsonPrimitive( formatDate( (Date)object ) );
}
else if( object.getClass().isEnum() ){
primitive = new JsonPrimitive( ((Enum>)object).name() );
}
if( serializeType && !(object instanceof Boolean) && !(object instanceof String) ){
JsonObject container = new JsonObject();
container.add( "c", new JsonPrimitive( getTypeName( type ) ) );
container.add( "v", primitive );
return container;
}
else{
return primitive;
}
}
private static Object deconvertSimple( JsonElement element, Class> type ){
String value = element.getAsString();
if( Boolean.class.equals( type )
|| boolean.class.equals( type )
|| element.getAsJsonPrimitive().isBoolean() ){
return Boolean.valueOf( value );
}
else if( Byte.class.equals( type ) || byte.class.equals( type ) ){
return Byte.valueOf( value );
}
else if( Short.class.equals( type ) || short.class.equals( type ) ){
return Short.valueOf( value );
}
else if( Integer.class.equals( type ) || int.class.equals( type ) ){
return Integer.valueOf( value );
}
else if( Long.class.equals( type ) || long.class.equals( type ) ){
return Long.valueOf( value );
}
else if( Float.class.equals( type ) || float.class.equals( type ) ){
return Float.valueOf( value );
}
else if( Double.class.equals( type ) || double.class.equals( type ) ){
return Double.valueOf( value );
}
else if( BigInteger.class.equals( type ) ){
return new BigInteger( value );
}
else if( BigDecimal.class.equals( type ) ){
return new BigDecimal( value );
}
else if( Date.class.equals( type ) ){
return parseDate( value );
}
else if( java.sql.Date.class.equals( type ) ){
return new java.sql.Date( parseDate(value).getTime() );
}
else if( java.sql.Time.class.equals( type ) ){
return new java.sql.Time( parseDate(value).getTime() );
}
else if( java.sql.Timestamp.class.equals( type ) ){
return new java.sql.Timestamp( parseDate(value).getTime() );
}
else if( type != null && type.isEnum() ){
@SuppressWarnings({"unchecked", "rawtypes"})
Class enumClazz = (Class)type;
@SuppressWarnings("unchecked")
Object result = Enum.valueOf( enumClazz, value );
return result;
}
else if( String.class.equals( type ) || element.getAsJsonPrimitive().isString() ){
return value;
}
throw new RuntimeException( "Unable to deconvert element " + element );
}
private static boolean isArray( Class> type ){
return type.isArray();
}
private static JsonElement convertArray( Object object, Class> type,
boolean serializeType ){
JsonArray array = new JsonArray();
Class> componentType = type.getComponentType();
if( boolean.class.equals( componentType ) ){
for( boolean element : (boolean[])object ){
array.add( convert( element, componentType, false ) );
}
}
else if( byte.class.equals( componentType ) ){
for( byte element : (byte[])object ){
array.add( convert( element, componentType, false ) );
}
}
else if( short.class.equals( componentType ) ){
for( short element : (short[])object ){
array.add( convert( element, componentType, false ) );
}
}
else if( int.class.equals( componentType ) ){
for( int element : (int[])object ){
array.add( convert( element, componentType, false ) );
}
}
else if( long.class.equals( componentType ) ){
for( long element : (long[])object ){
array.add( convert( element, componentType, false ) );
}
}
else if( float.class.equals( componentType ) ){
for( float element : (float[])object ){
array.add( convert( element, componentType, false ) );
}
}
else if( double.class.equals( componentType ) ){
for( double element : (double[])object ){
array.add( convert( element, componentType, false ) );
}
}
else{
Class> innermostComponentType = getInnermostComponentType( type );
boolean serializeElementType = !innermostComponentType.isPrimitive();
for( Object element : (Object[])object ){
array.add( convert( element,
element == null ? null : element.getClass(),
serializeElementType ) );
}
}
if( serializeType ){
JsonObject container = new JsonObject();
container.add( "c", new JsonPrimitive( getTypeName( type ) ) );
container.add( "v", array );
return container;
}
else{
return array;
}
}
private static Object deconvertArray( JsonElement element, Class> type ){
JsonArray array = element.getAsJsonArray();
Class> componentType = type.getComponentType();
if( boolean.class.equals( componentType ) ){
boolean[] result = new boolean[array.size()];
for( int i = 0; i < array.size(); i++ ){
result[i] = array.get( i ).getAsBoolean();
}
return result;
}
else if( byte.class.equals( componentType ) ){
byte[] result = new byte[array.size()];
for( int i = 0; i < array.size(); i++ ){
result[i] = array.get( i ).getAsByte();
}
return result;
}
else if( short.class.equals( componentType ) ){
short[] result = new short[array.size()];
for( int i = 0; i < array.size(); i++ ){
result[i] = array.get( i ).getAsShort();
}
return result;
}
else if( int.class.equals( componentType ) ){
int[] result = new int[array.size()];
for( int i = 0; i < array.size(); i++ ){
result[i] = array.get( i ).getAsInt();
}
return result;
}
else if( long.class.equals( componentType ) ){
long[] result = new long[array.size()];
for( int i = 0; i < array.size(); i++ ){
result[i] = array.get( i ).getAsLong();
}
return result;
}
else if( float.class.equals( componentType ) ){
float[] result = new float[array.size()];
for( int i = 0; i < array.size(); i++ ){
result[i] = array.get( i ).getAsFloat();
}
return result;
}
else if( double.class.equals( componentType ) ){
double[] result = new double[array.size()];
for( int i = 0; i < array.size(); i++ ){
result[i] = array.get( i ).getAsDouble();
}
return result;
}
else{
Object resultObject = Array.newInstance( componentType, array.size() );
Object[] result = (Object[])resultObject;
for( int i = 0; i < array.size(); i++ ){
Object arrayElement = deconvert( array.get( i ), componentType );
result[i] = arrayElement;
}
return result;
}
}
private static boolean isCollection( Class> type ){
return List.class.isAssignableFrom( type ) || Set.class.isAssignableFrom( type );
}
private static JsonElement convertCollection( Object object, Class> type, boolean serializeType, boolean serializeElementType ){
JsonArray array = new JsonArray();
for( Object element : (Collection>)object ){
array.add( convert( element, element == null ? null : element.getClass(), serializeElementType ) );
}
if( serializeType ){
JsonObject container = new JsonObject();
container.add( "c", new JsonPrimitive( getTypeName( type ) ) );
container.add( "v", array );
return container;
}
else{
return array;
}
}
private static Collection> deconvertCollection( JsonElement element, Class> type, Class> elementType ){
JsonArray array = element.getAsJsonArray();
if( ARRAYS_ASLIST_CLASS.equals( type ) ){
Object[] values = new Object[array.size()];
for( int i = 0; i < array.size(); i++ ){
Object arrayElement = deconvert( array.get( i ), elementType );
values[i] = arrayElement;
}
return Arrays.asList( values );
}
else if( SINGLETON_LIST_CLASS.equals( type ) ){
return Collections.singletonList( deconvert( array.get( 0 ),
elementType ) );
}
else if( SINGLETON_SET_CLASS.equals( type ) ){
return Collections.singleton( deconvert( array.get( 0 ), elementType ) );
}
else{
@SuppressWarnings("unchecked")
Collection