summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorThomas <>2023-05-15 08:34:22 +0200
committerThomas <>2023-05-15 08:34:22 +0200
commitbfeebfeb693f1f2ef0d2f6abb34bdf0aeffa3af4 (patch)
treeb31dcca809c9f03b7395d0053fa0acbdb5be7bfc
parent45a7ae26f0172c636e42b530561eb11efa0083d5 (diff)
downloadEAAF-Components-bfeebfeb693f1f2ef0d2f6abb34bdf0aeffa3af4.tar.gz
EAAF-Components-bfeebfeb693f1f2ef0d2f6abb34bdf0aeffa3af4.tar.bz2
EAAF-Components-bfeebfeb693f1f2ef0d2f6abb34bdf0aeffa3af4.zip
feat(utils): add custom Jackson (de)serialization for polymorph class structures
-rw-r--r--eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/json/EscapedJsonDeserializer.java97
-rw-r--r--eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/json/EscapedJsonSerializer.java73
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);
+ }
+}