使用 Jackson 反序列化重复键以列出
Posted
技术标签:
【中文标题】使用 Jackson 反序列化重复键以列出【英文标题】:Deserialize duplicate keys to list using Jackson 【发布时间】:2020-08-15 03:26:27 【问题描述】:我正在尝试将 json 反序列化为对象。但是,json 有重复的键。我无法更改 json,我想使用 Jackson 将重复键更改为列表。
这是我检索到的 json 示例:
"myObject":
"foo": "bar1",
"foo": "bar2"
这是反序列化后我想要的:
"myObject":
"foo": ["bar1","bar2"]
我这样创建了我的课程:
public class MyObject
private List<String> foo;
// constructor, getter and setter
我尝试使用objectMapper
中的DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY
,但它所做的只是获取最后一个键并将其添加到列表中,如下所示:
"myObject":
"foo": ["bar2"]
这是我的objectMapper
configuration:
new ObjectMapper().configure(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY, true);
有没有办法使用 Jackson 将重复键反序列化到列表中?
【问题讨论】:
我认为ObjectMapper
不可能,这不是一个有效的 json。你必须使用JsonParser
有一个similar question 接受答案suggests to use net.sf.json.JSONObject
***.com/a/48424996/3152549 应该有帮助!
@AlexRudenko 我不想添加另一个依赖项,但它看起来像另一个解决方案。
@Oleg 我可以试试JsonParser
,但我想我必须为此创建一个自定义反序列化器。如果可能的话,我想在不这样做的情况下找到解决方案。
【参考方案1】:
需要使用com.fasterxml.jackson.annotation.JsonAnySetter
注解:
import com.fasterxml.jackson.annotation.JsonAnySetter;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
public class JsonPathApp
public static void main(String[] args) throws Exception
File jsonFile = new File("./resource/test.json").getAbsoluteFile();
ObjectMapper mapper = new ObjectMapper();
Root root = mapper.readValue(jsonFile, Root.class);
root.getMyObject().getFoos().forEach(System.out::println);
class Root
private MyObject myObject;
// getters, setters, toString
class MyObject
private List<String> foos = new ArrayList<>();
@JsonAnySetter
public void manyFoos(String key, String value)
foos.add(value);
// getters, setters, toString
在Java
一侧,您有一个包含值的列表:
bar1
bar2
【讨论】:
它看起来像我要找的东西,但是你如何防止出现这个错误com.fasterxml.jackson.databind.exc.MismatchedInputException: Cannot deserialize instance of 'java.util.ArrayList' out of VALUE_STRING token
?我试图添加这个new ObjectMapper().configure(DeserializationFeature.FAIL_ON_READING_DUP_TREE_KEY, false)
,但它似乎不起作用
很好的回复,@MichałZiober!
@JsonMraz,这就是@JsonAnySetter
正在做的事情。请注意,属性名称是 foos
而不是 foo
。因此,默认情况下Jackson
不会触及它,并且所有其他未映射的属性都放在manyFoos
方法中。
@MichałZiober 抱歉,我没有注意到带有“s”的foos
。我用@JsonAnySetter
添加了它,它可以工作!非常感谢!
我尝试了这种方法,但我发现我的@JsonAnySetter
注释方法在我Sys.out
时没有得到重复的条目。我的意思是,当我在 @JsonAnySetter
注释方法中打印 key
和 value
时,我只看到一个条目,即使是重复的 key
也是最后一个值。就好像Jackson
在使用此方法之前已经忽略了重复条目。如果你得到,我已经在这里发布了我的问题。有机会请让我知道我该怎么做。 ***.com/questions/67413028/…【参考方案2】:
来自@Michał Ziober的补充回答:如果您需要处理混合单个值和列表/映射的情况:
"myObject":
"foo": "bar1",
"foo": ["bar2", "bar3"],
"foo": "bar": "baz"
这可以在同一个manyFoos
中完成:
@SuppressWarnings("unchecked")
@JsonAnySetter
public void manyFoos(String key, Object value)
System.out.println("key = " + key + " -> " + value.getClass() + ": " + value);
if (value instanceof String)
foos.add((String)value);
else if (value instanceof Collection<?>)
foos.addAll((Collection<? extends String>)value);
else if (value instanceof Map<?, ?>)
Map<? extends String, ? extends String> subFoo = (Map<? extends String, ? extends String>) value;
subFoo.entrySet().forEach(e -> foos.add(e.getKey() + ":" + e.getValue()));
那么结果会是:
"myObject" :
"foos" : [ "bar1", "bar2", "bar3", "bar:baz" ]
【讨论】:
我尝试了这种方法,但我发现我的@JsonAnySetter
注释方法在我Sys.out
时没有得到重复的条目。我的意思是当我为重复条目打印key
和value
时,我只看到重复键的最后一个值。就好像Jackson
在使用此方法之前已经忽略了重复条目。如果你得到,我已经在这里发布了我的问题。有机会请让我知道我该怎么做。 ***.com/questions/67413028/…以上是关于使用 Jackson 反序列化重复键以列出的主要内容,如果未能解决你的问题,请参考以下文章
使用 Jackson 和 Spring 将 JavaScript 数组反序列化为 Java LinkedHashSet 不会删除重复项
Jackson - 反序列化 JSON 字符串 - TypeReference 与 TypeFactory.constructCollectionType