【发布时间】:2016-04-26 03:04:07
【问题描述】:
我的 JSON 如下所示:
{"typeName":"test","field":{"name":"42"}}
我有两个反序列化器。第一个 (JsonDeserializer<EntityImpl>) 将检查 JSON 并提取由 typeName 属性提供的类型信息。
第二个反序列化器 (JsonDeserializer<TestField>) 用于反序列化 field 属性。此反序列化器需要知道先前提取的 typeName 值才能正常工作。
如何将类型信息从一个反序列化器传递到其他反序列化器?我尝试使用 DeserializationContext,但我不知道如何将上下文从反序列化器 A 传递到 B。
我当前的代码如下所示:
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;
}
}
测试:
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());
}
}
【问题讨论】:
-
这种类型的信息人工处理是在重新发明轮子。杰克逊已经可以做到这一点。看这篇文章cowtowncoder.com/blog/archives/2010/03/entry_372.html
-
我的类型信息不是指一个类。类型信息主要用于在 TestField 反序列化器中手动动态构建 POJO。因此,我无法使用杰克逊的类型系统。