反序列化不同类型到单个字段,使用Jackson

huangapple go评论75阅读模式
英文:

Deserialising different types to single field using Jackson

问题

我有一个如下的类我想用它来进行JSON反序列化

public interface MyObject {

    @JsonProperty("prop")
    String prop;

    @JsonProperty("value")
    Double value();        // 我希望这个方法返回一个double、字符串或者一个Map
}

然而我希望能够解析既包含双精度值的JSON比如

{
  prop: "myprop",
  value: 15.7
}

也能够解析包含非Double值的JSON比如字符串或者Map例如

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

我看过@JsonSubTypes注解但那似乎只对涉及到继承的情况有用在Jackson中能实现这个吗如果可以我该如何定义我的Java类以实现相同的效果
英文:

I have a following class that I want to use for deserializing 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
}

However, I want to be able to parse both the JSON with a double value

{
  prop: "myprop",
  value: 15.7
}

and a JSON with non Double value like a string or a map

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

I looked at @JsonSubTypes annotation, but that looks like only useful for the cases where inheritance is involved. Is it possible to do it in Jackson? If so how can I define my Java class to achieve the same?

答案1

得分: 5

总体而言我会不鼓励使用任意类型的数据点使用强类型带来了许多好处如果你愿意的话我可以谈谈这些好处然而由于你只谈到了反序列化可能是因为你正在读取别人生成的此类 JSON

解决方案非常简单使用 Object 字段

    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("{\"prop\": \"myprop\", \"value\": 15.7}", MyObject.class);
        Assert.assertEquals(15.7d, object1.value);
        final MyObject object2 = objectMapper.readValue("{\"prop\": \"myprop1\", \"value\": {\"attr1\": \"value1\", \"attr2\": 12.0}}", MyObject.class);
        Assert.assertTrue(object2.value instanceof Map);
    }
英文:

In general, I'd discourage the use of arbitrary types of data points. Having strong types gives plenty of benefits about which I can talk if you want. However, since you only talked about deserialization it might be that you are just reading such a JSON produced by someone else.

The solution is quite simply: use Object field.

public static class MyObject {

	@JsonProperty(&quot;prop&quot;)
	String prop;

	@JsonProperty(&quot;value&quot;)
	Object value;        // &lt;- object
}

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

答案2

得分: 1

你可以这样做:

@JsonDeserialize(using = ExampleDeserializer.class)
public class Example {
    String prod;
    Object value; /*此字段将会有多种类型*/
}

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() 或者 asText()...;
        }
        
        // ... 其他处理 ...
        
        return ex;
    }
}
英文:

What you could is something like this:

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

and the ExampleDeserializer would be like this:

public class ExampleDeserializer extends StdDeserializer&lt;Example&gt; {

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

    public ExampleDeserializer(Class&lt;?&gt; 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(&quot;prod&quot;) != null) {
          internalNode = tree.get(&quot;prod&quot;);
          prop = internalNode.get(&quot;prod&quot;).asText();
        }

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

}

答案3

得分: 0

你如果将不同类型命名为不同名称,处理起来会稍微容易一些,因此在JSON中应为:

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

或者

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

如果你按照这种方式做,那么你将拥有更多工具来强制执行有效性。

英文:

You'll have a slightly easier time of things if you have your different types as different names, so in JSON it should be:

{
  prop: &quot;my_map_prop&quot;,
  mapvalue: {
    &quot;attr1&quot;: &quot;value1&quot;,
    &quot;attr2&quot;: 12.0
  }
}

or

{
  prop: &quot;my_string_prop&quot;,
  stringvalue: &quot;string&quot;
}

If you do it like this you then have more tools at your disposal to enforce validity.

huangapple
  • 本文由 发表于 2020年9月24日 22:03:22
  • 转载请务必保留本文链接:https://go.coder-hub.com/64048165.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定