杰克逊自定义反序列化器打破了其他字段的反序列化

时间:2017-09-25 19:30:28

标签: java json serialization jackson

我需要将JsonArray反序列化为布尔值。如果数组存在且不为空,则该值应设置为" true"。 问题是,我的自定义反序列化器在功能上打破了其余字段的反序列化 - 它们被设置为null。

对象:

private static class TestObject {
    private String name;

    @JsonProperty("arr")
    @JsonDeserialize(using = Deserializer.class)
    private Boolean exists = null;

    private Integer logins;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Boolean getExists() {
        return exists;
    }

    public void setExists(boolean exists) {
        this.exists = exists;
    }

    public Integer getLogins() {
        return logins;
    }

    public void setLogins(Integer logins) {
        this.logins = logins;
    }

    @Override
    public String toString() {
        return "TestObject{" +
                "name='" + name + '\'' +
                ", exists=" + exists +
                ", logins=" + logins +
                '}';
    }
}

解串器:

public class Deserializer extends JsonDeserializer<Boolean> {
    @Override
    public Boolean deserialize(JsonParser jp, DeserializationContext ctxt)
        throws IOException {
        if (jp.getCurrentToken() == JsonToken.START_ARRAY) {
            return true;
        }
        return false;
    }
}

测试

@Test
public void test() throws JsonParseException, IOException {
    Boolean result = deserialize();
}

private Boolean deserialize() throws IOException, JsonParseException,
        JsonProcessingException {
    TestObject testObject = mapper.readValue("{\n" +
                    "  \"arr\": [\n" +
                    "     {\"value\": \"New\"}\n" +
                    "  ],\n" +
                    "  \"name\": \"name\",\n" +
                    "  \"logins\": 36" +
                    "}",
                    TestObject.class);

    System.out.println(testObject.toString());

    return testObject.getExists();
}

如果我删除&#34; arr&#34;数组或移动到Json的底部,一切都很好。如果我把它放在顶部 - TestObject{name='null', exists=true, logins=null}

有一个类似的问题(Jackson Custom Deserializer breaks default ones),但不幸的是它没有答案。 由于代码在重新排列Json时正常工作,所以看起来并不像自定义反序列化器用于所有字段,而是Jackson在执行自定义反序列化器时停止反序列化。

3 个答案:

答案 0 :(得分:1)

我的建议是不要将数组反序列化为布尔值。将数组反序列化为数组/列表/集,并让getExists返回arr != null或其他类似的数组。

例如,如果您要反序列化的JSON如下所示:

{
  "arr": [{"value": "New"}],
  "name": "name",
  "logins": 36
}

像这样写下您的实体:

private static class TestObject
{
  private List<Map<String, String>> arr;

  // other getters/setters removed for brevity

  public boolean getExists()
  {
    return arr !=null && !arr.isEmpty();
  }
}

答案 1 :(得分:1)

您的反序列化程序可能对数组的内容不感兴趣,但仍必须提升解析器上的读取标记。

您可以立即阅读arr的值,并根据集合的大小决定:

@Override
public Boolean deserialize(JsonParser jp, DeserializationContext ctxt)
        throws IOException {
    JsonNode node = jp.getCodec().readTree(jp);
    return node.size() != 0;
}

确定集合的 size 而不是集合的存在是必要的,因为你的字符串化对象包含一个arr或{{1}永远不会执行。在这种情况下,属性Deserializer.deserialize()将为exist。因此,表达的唯一可能语义不存在是一个空集合。

为了好奇,我尝试了第二种更明确的方法来保持解析器的正常运行。对于现实世界的使用,上述版本绝对是首选。

null

答案 2 :(得分:0)

实际上有第三种方法可以做到这一点,这符合我的情况 - 当JsonArray没有丢失时,它总是至少包含一个元素,所以我需要的是正确推进读取标记,然后返回true

if (jp.getCurrentToken().equals(JsonToken.START_ARRAY)) {
    while (!jp.getCurrentToken().equals(JsonToken.END_ARRAY)){
        jp.nextToken();
    }
}
return true;

除非您不需要数组内容进行其他工作,否则只需移动标记比readTree快几毫秒。