package alien4cloud.rest.utils; import java.io.IOException; import java.io.InputStream; import java.lang.reflect.TypeVariable; import java.util.Collection; import java.util.HashMap; import java.util.List; import java.util.Map; import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.JavaType; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.SerializationFeature; import alien4cloud.rest.model.RestResponse; import com.fasterxml.jackson.databind.type.ArrayType; import com.fasterxml.jackson.databind.type.TypeFactory; import com.fasterxml.jackson.databind.util.LRUMap; import lombok.extern.slf4j.Slf4j; /** * Simple utility for JSon processing. */ @Slf4j public final class JsonUtil { private static ObjectMapper getNewObjectMapper(boolean writeNullMapValues) { ObjectMapper mapper = new ObjectMapper(); mapper.enable(SerializationFeature.INDENT_OUTPUT); mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL); mapper.configure(SerializationFeature.WRITE_NULL_MAP_VALUES, writeNullMapValues); return mapper; } private static ObjectMapper createRestMapper() { ObjectMapper mapper = new RestMapper(); mapper.enable(SerializationFeature.INDENT_OUTPUT); mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL); mapper.configure(SerializationFeature.WRITE_NULL_MAP_VALUES, false); return mapper; } private static ObjectMapper getNewObjectMapper() { return getNewObjectMapper(false); } private JsonUtil() { } /** * Parse a {@link RestResponse} by using the specified dataType as the expected data object's class. * * @param responseAsString The {@link RestResponse} as a JSon String * @param dataType The type of the data object. * @return The parsed {@link RestResponse} object matching the given JSon. * @throws IOException In case of an IO error. */ public static <T> RestResponse<T> read(String responseAsString, Class<T> dataType) throws IOException { return read(responseAsString, dataType, createRestMapper()); } /** * Parse a {@link RestResponse} by using the specified dataType as the expected data object's class. * * @param responseAsString The {@link RestResponse} as a JSon String * @param dataType The type of the data object. * @param @param mapper the {@link ObjectMapper} to use * @return The parsed {@link RestResponse} object matching the given JSon. * @throws IOException In case of an IO error. */ public static <T> RestResponse<T> read(String responseAsString, Class<T> dataType, ObjectMapper mapper) throws IOException { JavaType innerType = constructType(dataType, mapper.getTypeFactory()); JavaType restResponseType = mapper.getTypeFactory().constructParametricType(RestResponse.class, innerType); return mapper.readValue(responseAsString, restResponseType); } private static <T> JavaType constructType(Class<T> dataType, TypeFactory typeFactory) { if (dataType == String.class || dataType == Boolean.TYPE || dataType == Integer.TYPE || dataType == Long.TYPE) { return typeFactory.constructSimpleType(dataType, null); } if (dataType.isArray()) { return typeFactory.constructArrayType(dataType.getComponentType()); } else if (dataType.isEnum()) { return typeFactory.constructSimpleType(dataType, new JavaType[0]); } else if (Map.class.isAssignableFrom(dataType)) { return typeFactory.constructRawMapType((Class<? extends Map>) dataType); } else if (Collection.class.isAssignableFrom(dataType)) { return typeFactory.constructRawCollectionType((Class<? extends Collection>) dataType); } TypeVariable<Class<T>>[] types = dataType.getTypeParameters(); JavaType[] javaTypes = new JavaType[types.length]; for (int i = 0; i < javaTypes.length; i++) { javaTypes[i] = TypeFactory.unknownType(); } return typeFactory.constructSimpleType(dataType, javaTypes); } /** * Parse a {@link RestResponse} without being interested in parameterized type * * @param responseAsString * @return * @throws IOException */ public static RestResponse<?> read(String responseAsString) throws IOException { return read(responseAsString, createRestMapper()); } /** * Parse a {@link RestResponse} without being interested in parameterized type * * @param responseAsString * @param mapper the {@link ObjectMapper} to use * @return * @throws IOException */ public static RestResponse<?> read(String responseAsString, ObjectMapper mapper) throws IOException { return mapper.readValue(responseAsString, RestResponse.class); } /** * Deserialize json text to object * * @param objectText * @param objectClass * @return * @throws IOException */ public static <T> T readObject(String objectText, Class<T> objectClass) throws IOException { return getNewObjectMapper().readValue(objectText, objectClass); } /** * Deserialize json stream to object * * @param jsonStream * @param objectClass * @return * @throws IOException */ public static <T> T readObject(InputStream jsonStream, Class<T> objectClass) throws IOException { return getNewObjectMapper().readValue(jsonStream, objectClass); } /** * Deserialize json text to object * * @param objectText * @return * @throws IOException */ public static <T> T readObject(String objectText) throws IOException { TypeReference<T> typeRef = new TypeReference<T>() { }; return getNewObjectMapper().readValue(objectText, typeRef); } /** * Serialize the given object in a JSon String. * * @param obj The object to serialize. * @return The JSon serialization of the given object. * @throws JsonProcessingException In case of a failure in serialization. */ public static String toString(Object obj) throws JsonProcessingException { return getNewObjectMapper().writeValueAsString(obj); } /** * Convert a map or list to an object * * @param raw a map or a list * @param targetType the target class * @param <T> the target * @return */ public static <T> T toObject(Object raw, Class<T> targetType) { return getNewObjectMapper().convertValue(raw, targetType); } /** * Serialize the given object in a JSon String (including null map entries). * * @param obj * The object to serialize. * @return The JSon serialization of the given object. * @throws JsonProcessingException * In case of a failure in serialization. */ public static String toVerboseString(Object obj) throws JsonProcessingException { return getNewObjectMapper(true).writeValueAsString(obj); } /** * Deserialize the given json string to a map * * @param json json text * @return map object * @throws IOException */ public static Map<String, Object> toMap(String json) throws IOException { ObjectMapper mapper = getNewObjectMapper(); JavaType mapStringObjectType = mapper.getTypeFactory().constructParametricType(HashMap.class, String.class, Object.class); return mapper.readValue(json, mapStringObjectType); } /** * Deserialize the given json string to a map * * @param json * @param keyTypeClass * @param valueTypeClass * @return * @throws IOException */ public static <K, V> Map<K, V> toMap(String json, Class<K> keyTypeClass, Class<V> valueTypeClass) throws IOException { return toMap(json, keyTypeClass, valueTypeClass, getNewObjectMapper()); } /** * Deserialize the given json string to a map * * @param json * @param keyTypeClass * @param valueTypeClass * @param mapper the {@link ObjectMapper} to use * @return * @throws IOException */ public static <K, V> Map<K, V> toMap(String json, Class<K> keyTypeClass, Class<V> valueTypeClass, ObjectMapper mapper) throws IOException { JavaType mapStringObjectType = mapper.getTypeFactory().constructParametricType(HashMap.class, keyTypeClass, valueTypeClass); return mapper.readValue(json, mapStringObjectType); } public static <V> V[] toArray(String json, Class<V> valueTypeClass) throws IOException { return toArray(json, valueTypeClass, createRestMapper()); } public static <V> V[] toArray(String json, Class<V> valueTypeClass, ObjectMapper mapper) throws IOException { JavaType arrayStringObjectType = mapper.getTypeFactory().constructArrayType(valueTypeClass); return mapper.readValue(json, arrayStringObjectType); } /** * Deserialize the given json string to a list * * @param json json text * @return list object * @throws IOException */ public static <T> List<T> toList(String json, Class<T> clazz) throws IOException { return toList(json, clazz, getNewObjectMapper()); } /** * Deserialize the given json string to a list * * @param json json text * @param mapper the {@link ObjectMapper} to use * @return list object * @throws IOException */ public static <T> List<T> toList(String json, Class<T> clazz, ObjectMapper mapper) throws IOException { JavaType type = mapper.getTypeFactory().constructCollectionType(List.class, clazz); return mapper.readValue(json, type); } public static <T> List<T> toList(String json, Class<T> elementClass, Class<?> elementGenericClass) throws IOException { return toList(json, elementClass, elementGenericClass, getNewObjectMapper()); } public static <T> List<T> toList(String json, Class<T> elementClass, Class<?> elementGenericClass, ObjectMapper mapper) throws IOException { JavaType elementType = mapper.getTypeFactory().constructParametricType(elementClass, elementGenericClass); JavaType listType = mapper.getTypeFactory().constructCollectionType(List.class, elementType); return mapper.readValue(json, listType); } }