使用jackson在java中嵌套自定义反序列化

ro.*_*nin 4 java serialization json jackson deserialization

我一直在想如何正确解决这个问题

我有一个这样的数据模型:

Class B
 String fieldB1;
 String fieldB2;

Class A
 String fieldA1;
 String fieldA2;
 List<B> fieldA3;
Run Code Online (Sandbox Code Playgroud)

(然后是另一个第三个类,它具有与另一个具有相同层次结构的字段和 A 对象列表,但为了简单起见,让我们坚持使用 A 和 B)

现在在另一方面,我必须在具有相同名称和参数的类中反序列化这些类,只是具有不同的数据类型

所以 ^ 必须读作:

Class B
 int fieldB1;
 double fieldB2;

Class A
 float fieldA1;
 float fieldA2;
 List<B> fieldA3;
Run Code Online (Sandbox Code Playgroud)

由于我没有经验,我的第一个猜测是在 jackson 中为 A 和 B 编写自定义反序列化器,当我反序列化像 B 这样没有使用自定义反序列化方法引用其他类的类时,转换很容易。

但是为 A 类创建自定义解串器呢?当我必须反序列化 fieldA3,也就是 B 对象的列表时,我应该如何操作?是否应该尝试在 ClassACustomDeserializer 中以某种方式调用 ClassBCustomDeserializer?怎么做?或者是否有另一种更简单的解决方案来告诉杰克逊根据我的个人映射将一些其他类型的字符串字段转换为其他类型?

这就是我反序列化 B 的方式

public class BDeserializer extends StdDeserializer<B> { 

    public BDeserializer() { 
        this(null); 
    } 

    public BDeserializer(Class<?> vc) { 
        super(vc); 
    }

    @Override
    public B deserialize(JsonParser jp, DeserializationContext ctxt) 
      throws IOException, JsonProcessingException {
        JsonNode node = jp.getCodec().readTree(jp);

        int fieldB1= node.findValue("fieldB1").asInt();
        double fieldB2= node.findValue("fieldB2").asDouble();

        return new B(fieldB1,fieldB2);
    }
}
Run Code Online (Sandbox Code Playgroud)

zep*_*lin 7

Jackson 足够聪明,可以将文本值转换为适当的数字类型,因此它应该能够反序列化 JSON,例如:

{ "fieldB1": 10, "fieldB2" : "0.333" }
Run Code Online (Sandbox Code Playgroud)

给你的

Class B
    int fieldB1;
    double fieldB2;
Run Code Online (Sandbox Code Playgroud)

很好,即使不使用自定义解串器。

如果您想坚持使用自定义反序列化器,无论出于何种原因,您都可以使用 JsonNode.traverse() 创建一个子解析器:

JsonParser parser = node.findValue("fieldA3").traverse();
parser.setCodec(jp.getCodec());
List<B> list = parser.readValueAs(new TypeReference<List<B>>() {});
Run Code Online (Sandbox Code Playgroud)

或自己导航令牌流,而不是使用find

while(jp.nextToken() != JsonToken.END_OBJECT) {
    if(jp.currentToken() == JsonToken.FIELD_NAME) {
       switch (jp.getCurrentName()) {
           //...
           case "fieldA3":
             jp.nextToken();
             list=jp.readValueAs(new TypeReference<List<ClassB>>() {}));
           break;
       }
    }
}
Run Code Online (Sandbox Code Playgroud)

如果性能受到关注,后者应该更有效。