diff options
author | Thomas <> | 2023-05-15 08:34:22 +0200 |
---|---|---|
committer | Thomas <> | 2023-05-15 08:34:22 +0200 |
commit | bfeebfeb693f1f2ef0d2f6abb34bdf0aeffa3af4 (patch) | |
tree | b31dcca809c9f03b7395d0053fa0acbdb5be7bfc | |
parent | 45a7ae26f0172c636e42b530561eb11efa0083d5 (diff) | |
download | EAAF-Components-bfeebfeb693f1f2ef0d2f6abb34bdf0aeffa3af4.tar.gz EAAF-Components-bfeebfeb693f1f2ef0d2f6abb34bdf0aeffa3af4.tar.bz2 EAAF-Components-bfeebfeb693f1f2ef0d2f6abb34bdf0aeffa3af4.zip |
feat(utils): add custom Jackson (de)serialization for polymorph class structures
2 files changed, 170 insertions, 0 deletions
diff --git a/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/json/EscapedJsonDeserializer.java b/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/json/EscapedJsonDeserializer.java new file mode 100644 index 00000000..5a081ec5 --- /dev/null +++ b/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/json/EscapedJsonDeserializer.java @@ -0,0 +1,97 @@ +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. + * + * <p> + * Code was an example from stack-overflow. <br> + * <br> + * This implementation checks if the generic class type is compatible to + * original interface, by using <code>Class.isAssignableFrom(...).</code> + * </p> + * + * @author tlenz + * + */ +public class EscapedJsonDeserializer extends JsonDeserializer<Object> implements ContextualDeserializer { + private final Map<JavaType, JsonDeserializer<Object>> 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<Object> { + 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); + + } + } + } +} diff --git a/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/json/EscapedJsonSerializer.java b/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/json/EscapedJsonSerializer.java new file mode 100644 index 00000000..a79f4d90 --- /dev/null +++ b/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/json/EscapedJsonSerializer.java @@ -0,0 +1,73 @@ +package at.gv.egiz.eaaf.core.impl.json; + +import java.io.IOException; +import java.io.StringWriter; +import java.util.Collection; + +import com.fasterxml.jackson.core.JsonFactory; +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.SerializerProvider; +import com.fasterxml.jackson.databind.jsontype.TypeSerializer; +import com.fasterxml.jackson.databind.node.ObjectNode; +import com.fasterxml.jackson.databind.node.TextNode; +import com.fasterxml.jackson.databind.ser.std.StdSerializer; + + +/** + * Custom Jackson Serializer to support generic types and serialize them as + * escaped Strings. + * + * <p> + * Code was an example from stack-overflow. + * </p> + * + * @author tlenz + * + */ +public class EscapedJsonSerializer extends StdSerializer<Object> { + private static final long serialVersionUID = 7334472154148003249L; + + public EscapedJsonSerializer() { + super((Class<Object>) null); + } + + @Override + public void serialize(Object value, JsonGenerator gen, SerializerProvider provider) throws IOException { + StringWriter str = new StringWriter(); + JsonGenerator tempGen = new JsonFactory().setCodec(gen.getCodec()).createGenerator(str); + if (value instanceof Collection || value.getClass().isArray()) { + tempGen.writeStartArray(); + if (value instanceof Collection) { + for (Object it : (Collection) value) { + writeTree(gen, it, tempGen); + } + } else if (value.getClass().isArray()) { + for (Object it : (Object[]) value) { + writeTree(gen, it, tempGen); + } + } + tempGen.writeEndArray(); + } else { + provider.defaultSerializeValue(value, tempGen); + } + tempGen.flush(); + gen.writeString(str.toString()); + } + + @Override + public void serializeWithType(Object value, JsonGenerator gen, SerializerProvider serializers, + TypeSerializer typeSer) throws IOException { + StringWriter str = new StringWriter(); + JsonGenerator tempGen = new JsonFactory().setCodec(gen.getCodec()).createGenerator(str); + writeTree(gen, value, tempGen); + tempGen.flush(); + gen.writeString(str.toString()); + } + + private void writeTree(JsonGenerator gen, Object it, JsonGenerator tempGen) throws IOException { + ObjectNode tree = ((ObjectMapper) gen.getCodec()).valueToTree(it); + tree.set("@class", new TextNode(it.getClass().getName())); + tempGen.writeTree(tree); + } +} |