package at.gv.egiz.eaaf.core.impl.json;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.TreeNode;
import com.fasterxml.jackson.databind.BeanProperty;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.JsonDeserializer;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.deser.ContextualDeserializer;
import com.fasterxml.jackson.databind.jsontype.TypeDeserializer;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.fasterxml.jackson.databind.node.TextNode;
/**
* Custom Jackson Deserializer to support generic types serialized as escaped
* Strings.
*
*
* Code was an example from stack-overflow.
*
* This implementation checks if the generic class type is compatible to
* original interface, by using Class.isAssignableFrom(...).
*
*
* @author tlenz
*
*/
public class EscapedJsonDeserializer extends JsonDeserializer implements ContextualDeserializer {
private final Map> cachedDeserializers = new HashMap<>();
@Override
public Object deserialize(JsonParser p, DeserializationContext ctxt) throws IOException,
JsonProcessingException {
throw new IllegalArgumentException(
"EscapedJsonDeserializer should delegate deserialization for concrete class");
}
@Override
public JsonDeserializer> createContextual(DeserializationContext ctxt, BeanProperty property)
throws JsonMappingException {
JavaType type = (ctxt.getContextualType() != null) ? ctxt.getContextualType()
: property.getMember().getType();
return cachedDeserializers.computeIfAbsent(type, (a) -> new InnerDeserializer(type));
}
private static class InnerDeserializer extends JsonDeserializer {
private final JavaType javaType;
private InnerDeserializer(JavaType javaType) {
this.javaType = javaType;
}
@Override
public Object deserialize(JsonParser p, DeserializationContext ctxt) throws IOException,
JsonProcessingException {
String string = p.readValueAs(String.class);
return ((ObjectMapper) p.getCodec()).readValue(string, javaType);
}
@Override
public Object deserializeWithType(JsonParser p, DeserializationContext ctxt,
TypeDeserializer typeDeserializer)
throws IOException {
String str = p.readValueAs(String.class);
TreeNode root = ((ObjectMapper) p.getCodec()).readTree(str);
try {
Class clz = Class.forName(((TextNode) root.get("@class")).asText());
if (this.javaType.getRawClass().isAssignableFrom(clz)) {
// remove '@class' before deserialization because it's unknown in target class
if (root.isObject()) {
((ObjectNode) root).remove("@class");
}
Object newJsonNode = p.getCodec().treeToValue(root, clz);
return newJsonNode;
} else {
throw new RuntimeException("Class looks supect because it does not match to interface.");
}
} catch (ClassNotFoundException e) {
throw new RuntimeException(e);
}
}
}
}