具有不同对象的类型的 Jackson 反序列化

2022-09-03 09:52:21

我有一个来自Web服务的结果,该结果返回布尔值或单例映射,例如

布尔结果:

{
    id: 24428,
    rated: false
}

地图结果:

{
    id: 78,
    rated: {
        value: 10
    }
}

单独来看,我可以很容易地映射这两个,但是我该如何一般地做到这一点呢?

基本上,我想将它映射到一个类,例如:

public class Rating {
    private int id;
    private int rated;
    ...
    public void setRated(?) {
        // if value == false, set rated = -1;
        // else decode "value" as rated
    }
}

所有多态示例都用于基于数据中的属性进行映射,但在本例中我没有该选项。@JsonTypeInfo


编辑
更新的代码部分:
@JsonProperty("rated")
public void setRating(JsonNode ratedNode) {
    JsonNode valueNode = ratedNode.get("value");
    // if the node doesn't exist then it's the boolean value
    if (valueNode == null) {
        // Use a default value
        this.rating = -1;
    } else {
        // Convert the value to an integer
        this.rating = valueNode.asInt();
    }
}

答案 1

不 不 不。您不必编写自定义反序列化程序。只需先使用“非类型化”映射:

public class Response {
  public long id;
  public Object rated;
}
// OR
public class Response {
  public long id;
  public JsonNode rated;
}
Response r = mapper.readValue(source, Response.class);

它给出了“评级”的值或“评级”(使用第一种方法);或第二种情况。Booleanjava.util.MapJsonNode

由此,您可以按原样访问数据,或者更有趣的是,转换为实际值:

if (r.rated instanced Boolean) {
    // handle that
} else {
    ActualRated actual = mapper.convertValue(r.rated, ActualRated.class);
}
// or, if you used JsonNode, use "mapper.treeToValue(ActualRated.class)

还有其他种类的方法 - 使用创建者“ActualRated(布尔)”,让实例从POJO标量构造。但我认为上面应该有效。


答案 2

您必须编写自己的反序列化程序。它可能看起来像这样:

@SuppressWarnings("unchecked")
class RatingJsonDeserializer extends JsonDeserializer<Rating> {

    @Override
    public Rating deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException, JsonProcessingException {
        Map<String, Object> map = jp.readValueAs(Map.class);

        Rating rating = new Rating();
        rating.setId(getInt(map, "id"));
        rating.setRated(getRated(map));

        return rating;
    }

    private int getInt(Map<String, Object> map, String propertyName) {
        Object object = map.get(propertyName);

        if (object instanceof Number) {
            return ((Number) object).intValue();
        }

        return 0;
    }

    private int getRated(Map<String, Object> map) {
        Object object = map.get("rated");
        if (object instanceof Boolean) {
            if (((Boolean) object).booleanValue()) {
                return 0; // or throw exception
            }

            return -1;
        }

        if (object instanceof Map) {
            return getInt(((Map<String, Object>) object), "value");
        }

        return 0;
    }
}

现在你必须告诉 Jackson 将这个反序列化程序用于类:Rating

@JsonDeserialize(using = RatingJsonDeserializer.class)
class Rating {
...
}

简单用法:

ObjectMapper objectMapper = new ObjectMapper();
System.out.println(objectMapper.readValue(json, Rating.class));

以上程序打印:

Rating [id=78, rated=10]

对于 JSON:

{
    "id": 78,
    "rated": {
        "value": 10
    }
}

和打印件:

Rating [id=78, rated=-1]

对于 JSON:

{
    "id": 78,
    "rated": false
}

推荐