My JSON looks like this:
{"typeName":"test","field":{"name":"42"}} I have two deserializers. The first one (JsonDeserializer<EntityImpl>) will examine the JSON and extract a type information which is provided by the typeName property.
The second deserializer (JsonDeserializer<TestField>) is used to deserialize the field property. This deserializer needs to know the previously extracted typeName value in order to work correctly.
How can i pass-along the type information from one deserializer to other deserializers? I tried to use DeserializationContext but i don't know how to pass along the Context from deserializer A to B.
My current code looks like this:
EntityImpl.java:
package de.jotschi.test; public class EntityImpl implements Entity { private String typeName; private TestField field; public String getTypeName() { return typeName; } public void setTypeName(String typeName) { this.typeName = typeName; } public TestField getField() { return field; } public void setField(TestField field) { this.field = field; } } TestField.java:
package de.jotschi.test; public class TestField { String name; public String getName() { return name; } public void setName(String name) { this.name = name; } } Test:
package de.jotschi.test; import java.io.IOException; import java.util.HashMap; import java.util.Map; import org.junit.Test; import com.fasterxml.jackson.annotation.JsonInclude.Include; import com.fasterxml.jackson.core.JsonParser; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.core.ObjectCodec; import com.fasterxml.jackson.core.Version; import com.fasterxml.jackson.databind.BeanProperty; import com.fasterxml.jackson.databind.DeserializationContext; import com.fasterxml.jackson.databind.DeserializationFeature; import com.fasterxml.jackson.databind.InjectableValues; import com.fasterxml.jackson.databind.JsonDeserializer; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.module.SimpleModule; import de.jotschi.test.EntityImpl; import de.jotschi.test.TestField; public class TestMapper2 { private InjectableValues getInjectableValue() { InjectableValues values = new InjectableValues() { @Override public Object findInjectableValue(Object valueId, DeserializationContext ctxt, BeanProperty forProperty, Object beanInstance) { if ("data".equals(valueId.toString())) { return new HashMap<String, String>(); } return null; } }; return values; } @Test public void testMapper() throws IOException { ObjectMapper mapper = new ObjectMapper(); SimpleModule idAsRefModule = new SimpleModule("ID-to-ref", new Version(1, 0, 0, null)); idAsRefModule.addDeserializer(EntityImpl.class, new JsonDeserializer<EntityImpl>() { @Override public EntityImpl deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException, JsonProcessingException { Map<String, String> dataMap = (Map) ctxt.findInjectableValue("data", null, null); System.out.println("Value: " + dataMap.get("test")); ObjectCodec codec = jp.getCodec(); JsonNode node = codec.readTree(jp); String type = node.get("typeName").asText(); dataMap.put("typeName", type); // How to pass on type information to TestField deserializer? The context is not reused for the next deserializer. // I assume that readValueAs fails since the codec.readTree method has already been invoked. //return jp.readValueAs(EntityImpl.class); // Alternatively the treeToValue method can be invoked in combination with the node. Unfortunately all information about the DeserializationContext is lost. I assume new context will be created. // How to reuse the old context? return codec.treeToValue(node, EntityImpl.class); } }); idAsRefModule.addDeserializer(TestField.class, new JsonDeserializer<TestField>() { @Override public TestField deserialize(JsonParser p, DeserializationContext ctxt) throws IOException, JsonProcessingException { // Access type from context Map<String, String> dataMap = (Map) ctxt.findInjectableValue("data", null, null); System.out.println(dataMap.get("typeName")); ObjectCodec codec = p.getCodec(); JsonNode node = codec.readTree(p); return codec.treeToValue(node, TestField.class); } }); mapper.registerModule(idAsRefModule); mapper.setSerializationInclusion(Include.NON_NULL); mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); // Setup the pojo EntityImpl impl = new EntityImpl(); impl.setTypeName("test"); TestField testField = new TestField(); testField.setName("42"); impl.setField(testField); // POJO -> JSON String json = mapper.writeValueAsString(impl); System.out.println(json); // JSON -> POJO Entity obj = mapper.reader(getInjectableValue()).forType(EntityImpl.class).readValue(json); System.out.println(obj.getClass().getName()); } }