我想使用Jackson将对象动态反序列化为适当的java类,但是我很难正确地配置Jackson。
我有以下简化模型(为了简洁起见省略了getter/setters):
class GeneralObject {
public String objType;
public String commonProp;
public GeneralObject nestedObject;
// map for additional properties, so that I can re-serialize the full object later
public Map<String, JsonNode> additionalFields = new HashMap<>();
@JsonAnyGetter
public Map<String, JsonNode> getAdditionalFields() {
return additionalFields;
}
@JsonAnySetter
public void addAdditionalField(String fieldName, JsonNode value) {
this.additionalFields.put(fieldName, value);
}
}
class SpecialObject extends GeneralObject {
public String specialProp;
}在现实中,有不同类型的“特殊对象”,我希望能够在未来的需要时增加更多。
jsons看起来是这样的(我从外部来源获得它们,我不能更改发送它们的格式):
{
"objType": "someType1",
"commonProp": "example1..."
}{
"objType": "SPECIAL",
"commonProp": "example2...",
"specialProp": "more example"
}{
"objType": "someOtherType",
"commonProp": "example3...",
"nestedObject": {
"objType": "SPECIAL",
"commonProp": "example2...",
"specialProp": "more example"
}
}我目前正像这样解析它们:
ObjectMapper mapper = new ObjectMapper();
String objString = "{\"objType\": \"SPECIAL\", \"commonProp\": \"...\", \"specialProp\": \"more example\"}";
GeneralObject genObj = mapper.readValue(objString, GeneralObject.class);
if (genObj.objType.equals("SPECIAL")) {
genObj = mapper.readValue(objString, SpecialObject.class);
}
// Some business-logic: If SPECIAL, then this cast is required to work:
System.out.println(((SpecialObject) genObj).specialProp);这适用于顶层对象,但不适用于嵌套对象。例如,如果嵌套对象是特殊对象,则仍然将其反序列化为公共对象。
我想要做的是告诉杰克逊:“无论嵌套级别,如果objType=SPECIAL,使用SpecialObject,否则使用GeneralObject”。我查看了多态反序列化并尝试使用@JsonSubTypes,但未能正确设置此逻辑。如何确保将特殊对象反序列化为适当的类,即使它们是嵌套的?
发布于 2021-07-08 11:25:24
我首先从这个要旨获得灵感,并试图用自定义TypeIdResolver来解决这个问题。不幸的是,这存在不正确地反序列化objType的问题(请参阅这个答案的第一个版本)。
然后,我从这个答案获得灵感,转而使用自定义的Deserializer。
class CustomDeserializer extends StdDeserializer<GeneralObject> {
private static final String SPECIAL = "\"SPECIAL\"";
protected CustomDeserializer() {
super(GeneralObject.class);
}
@Override
public GeneralObject deserialize(JsonParser p, DeserializationContext ctx) throws IOException {
TreeNode node = p.readValueAsTree();
// Select appropriate class based on "resourceType"
TreeNode objTypeNode = node.get("objType");
if (null == objTypeNode) {
throw new JsonParseException(p, "field \"objType\" is missing!");
}
if (!objTypeNode.isValueNode()) {
throw new JsonParseException(p, "field \"objType\" must be a String.");
}
String objType = objTypeNode.toString();
Class<? extends GeneralObject> clazz;
if (objType.equals(SPECIAL)) {
clazz = SpecialObject.class;
} else {
clazz = RecursionStopper.class;
}
return p.getCodec().treeToValue(node, clazz);
}
}它检查.objType的内容,并发出应该用于反序列化的适当(子)-class。反序列化器需要在GeneralObject上注册,例如使用以下注释:
@JsonDeserialize(using = CustomDeserializer.class)
class GeneralObject {
...
}为了阻止无限递归循环的发生,必须对所有子类进行注释,以避免使用此自定义反序列化器,并且我们需要引入一个助手类来停止GeneralObject的递归。
@JsonDeserialize(using = JsonDeserializer.None.class)
class SpecialObject extends GeneralObject {
public String specialProp;
}
@JsonDeserialize(using = JsonDeserializer.None.class)
class RecursionStopper extends GeneralObject {
// this class intentionally empty
}反序列化也适用于嵌套对象:
ObjectMapper mapper = new ObjectMapper();
String objString = "{\n" +
" \"objType\": \"someObjType\",\n" +
" \"commonProp\": \"example3...\",\n" +
" \"nestedObject\": {\n" +
" \"objType\": \"SPECIAL\",\n" +
" \"commonProp\": \"example2...\",\n" +
" \"specialProp\": \"more example\"\n" +
" }\n" +
"}";
GeneralObject genObj = mapper.readValue(objString, GeneralObject.class);
System.out.println(((SpecialObject) genObj.nestedObject).specialProp);https://stackoverflow.com/questions/68298083
复制相似问题