英文:
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("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);
}
答案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<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()...;
}
}
答案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: "my_map_prop",
mapvalue: {
"attr1": "value1",
"attr2": 12.0
}
}
or
{
prop: "my_string_prop",
stringvalue: "string"
}
If you do it like this you then have more tools at your disposal to enforce validity.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论