使用Jackson将不同类型反序列化为单个字段

问题描述

我有一个要用于反序列化JSON的以下类

public interface MyObject {

    @JsonProperty("prop")
    String prop;

    @JsonProperty("value")
    Double value();        // Need this to be either a double or a string or a Map
}

但是,我希望能够同时解析具有双精度值的JSON

{
  prop: "myprop",value: 15.7
}

以及具有非Double值(如字符串或地图)的JSON

{
  prop: "myprop1",value: {
    "attr1": "value1","attr2": 12.0
  }
}

我查看了@JsonSubTypes批注,但这似乎仅对涉及继承的情况有用。有可能在杰克逊做到吗?如果是这样,我如何定义我的java类以实现相同的目的?

解决方法

通常,我不鼓励使用任意类型的数据点。如果您愿意,拥有强类型可以带来很多好处。但是,由于您仅谈论反序列化,可能是您正在阅读其他人生成的此类JSON。

解决方案非常简单:使用“对象”字段。

public static class MyObject {

    @JsonProperty("prop")
    String prop;

    @JsonProperty("value")
    Object value;        // <- object
}

@Test
public void testUnknownType() throws JsonProcessingException {
    final ObjectMapper objectMapper = new ObjectMapper();
    final MyObject object1 = objectMapper.readValue("{\n" +
        "  \"prop\": \"myprop\",\n" +
        "  \"value\": 15.7\n" +
        "}",MyObject.class);
    Assert.assertEquals(15.7d,object1.value);
    final MyObject object2 = objectMapper.readValue("{\n" +
        "  \"prop\": \"myprop1\",\n" +
        "  \"value\": {\n" +
        "    \"attr1\": \"value1\",\n" +
        "    \"attr2\": 12.0\n" +
        "  }\n" +
        "}",MyObject.class);
    Assert.assertTrue(object2.value instanceof Map);
}
,

您可能会这样:

@JsonDeserialize(using = ExampleDeserializer.class)
public class Example{
String prod;
Object value; /*this field will takes multiple types*/
}

和ExampleDeserializer如下:

public class ExampleDeserializer extends StdDeserializer<Example> {

    public ExampleDeserializer() {
        super(Example.class);
    }

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

@Override
    public Example deserialize(JsonParser p,DeserializationContext ctx) throws IOException {
        Example ex = new Example();
        ObjectCodec mapper = p.getCodec();
        if (mapper == null) mapper = new ObjectMapper();
        JsonNode tree = mapper.readTree(p);
        JsonNode internalNode;

        if (tree.get("prod") != null) {
          internalNode = tree.get("prod");
          prop = internalNode.get("prod").asText();
        }

       if (tree.get("value") != null) {
          internalNode = tree.get("value");
          value = (Double) internalNode.get("value").asDouble() or asText()...;
        }

}
,

如果您将不同的类型用作不同的名称,那么事情的时间会稍微容易一些,因此在JSON中应该是这样:

{
  prop: "my_map_prop",mapvalue: {
    "attr1": "value1","attr2": 12.0
  }
}

{
  prop: "my_string_prop",stringvalue: "string"
}

如果您这样做,便可以使用更多工具来增强有效性。