使用Jackson Object Mapper将Java Map转换为JSON并保持键的顺序

10
我正在使用jackson.map.ObjectMapper API将map转换为json字符串。我正在使用writeValueAsString方法来实现此操作。
我将基于值排序的map传递给writeValueAsString方法。然后得到的JSON字符串是按键重新排序的。
有没有一种方法可以使用jackson将map转换为JSON字符串,而不会破坏地图中项目的顺序。
我尝试将Feature.SORT_PROPERTIES_ALPHABETICALLY设置为false,但根据文档,它仅适用于POJO类型。
有任何想法实现所需的行为。

1
一个通用的映射并不保证键的顺序,所以我不知道你怎么能做到这一点。 - hd1
你需要确保使用的是维护插入顺序的映射,即TreeMap而不是HashMap。 - Mikkel Løkke
我只使用了Treemmap,并且值已按我想要的方式排序。但是,一旦我将我的treemap(实际上是包含多个树图的对象)传递给Jackson API以获取JSON字符串,所有我的映射值都会基于键重新排序。 - Ani
请纠正我,如果我错了的话,但我不知道有任何正确创建按值排序的Map的方法,这完全兼容java SortedMap接口规范。一组成对的SortedSet会很好。但是Map将是错误的。在这样的地图上更新值可能会破坏一切。比较值的Comparator<Key>也违反了一些规则!请考虑使用SortedSet of Pairs。 - bartosz.r
1个回答

16

使用Jackson 2.3.1(之前的版本未知),您可以序列化SortedMap,例如TreeMap,顺序将得到保留。

这里是在junit 4中的一个示例:

    @Test
public void testSerialize() throws JsonProcessingException{
    ObjectMapper om = new ObjectMapper();
    om.configure(SerializationFeature.WRITE_NULL_MAP_VALUES,false);
    om.configure(SerializationFeature.INDENT_OUTPUT,true);
    om.setSerializationInclusion(Include.NON_NULL);

    SortedMap<String,String> sortedMap = new TreeMap<String,String>();
    Map<String,String> map = new HashMap<String,String>();
    map.put("aaa","AAA");

    map.put("bbb","BBB");
    map.put("ccc","CCC");
    map.put("ddd","DDD");

    sortedMap.putAll(map);

    System.out.println(om.writeValueAsString(map));

    System.out.println(om.writeValueAsString(sortedMap));


}

这里是结果:

使用地图

{
  "aaa" : "AAA",
  "ddd" : "DDD",
  "ccc" : "CCC",
  "bbb" : "BBB"
}

使用SortedMap

{
  "aaa" : "AAA",
  "bbb" : "BBB",
  "ccc" : "CCC",
  "ddd" : "DDD"
}

第一个使用Map的序列化不会按顺序排列,第二个使用TreeMap的序列化将按键的字母顺序排序。您可以向treeMap传递Comparator以获得不同的顺序。

编辑:即使这不是SortedMap,但在Jackson上使用LinkedHashMap()也可以工作。这是一种实现了保留插入到映射中的键的顺序的Map。这可能是您正在寻找的内容。


2
感谢您抽出时间来阅读。我已经尝试了treemap和linkedhashmap两种方式。我正在配置我的mapper,以便不使用以下配置属性对映射进行排序。SORTED_MAPPER.configure(SerializationConfig.Feature.SORT_PROPERTIES_ALPHABETICALLY, false); 当我将一个map(treemap或linked hashmap)传递给writeValueAsString方法时,这个方法完全正常运行。例如:SORTED_MAPPER.writeValueAsString(map); - Ani
然而,我有10个不同的地图需要转换为JSON,因此我无法将地图对象传递给此方法。因此,我将所有地图放在一个对象中,并将此对象作为SORTED_MAPPER.writeValueAsString(myObject)传递给该方法,其中myObject包含所有地图;由于某种原因,Jackson API无法在这种情况下配置地图并根据键重新排序所有地图。 - Ani
Jackson 在某些边缘情况下存在一些问题,最好实现自己的序列化器。(http://www.baeldung.com/jackson-custom-serialization) - Martin Kersten
1
@MartinKersten 是的,一个专门针对Map的自定义Jackson序列化器。一开始我还担心你会建议在没有任何库的情况下完成所有JSON序列化。你有关于这个边缘案例的更多信息吗?它是否涉及到Map序列化? - pdem
1
我支持的团队最终采用自定义序列化程序的情况有几种,或者当我负责实现我们自己的JSON序列化机制时。JSON相当简单,直接支持它很容易实现。我不喜欢注释和隐藏的魔法。序列化关注数据本身,而数据是我们日常编程汤的主要成分。因此,我喜欢一个数据对象能够自我序列化,而不是“发现”如何序列化它。每当你有(模式)演变的事情变得混乱。所以自定义几乎总是最好的选择。团队通常会将其视为解脱。 - Martin Kersten

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接