从 LinkedHashMap 构建有序 JSON 字符串

Posted

技术标签:

【中文标题】从 LinkedHashMap 构建有序 JSON 字符串【英文标题】:Building ordered JSON String from LinkedHashMap 【发布时间】:2015-06-12 00:45:41 【问题描述】:

我需要按插入顺序排列键/值对,因此我选择使用LinkedHashMap 而不是HashMap。但我需要将LinkedHashMap 转换为JSON 字符串,其中LinkedHashMap 中的顺序保留在字符串中。

但目前我正在通过以下方式实现它:

    首先将 LinkedHashMap 转换为 JSON。

    然后将 JSON 转换为字符串。

    import java.util.LinkedHashMap;
    import java.util.Map;
    
    import org.json.JSONObject;
    
    public class cdf 
        public static void main(String[] args) 
            Map<String,String > myLinkedHashMap =  new LinkedHashMap<String, String>();
            myLinkedHashMap.put("1","first");
            myLinkedHashMap.put("2","second");
            myLinkedHashMap.put("3","third");
    
            JSONObject json = new JSONObject(myLinkedHashMap);
            System.out.println(json.toString());
        
    
    

输出是:

"3":"third","2":"second","1":"first" . 

但我希望它按插入键的顺序,如下所示:

"1":"first","2":"second","3":"third"

一旦我将LinkedHashMap 转换为 JSON,它就会失去它的顺序(很明显 JSON 没有顺序的概念),因此字符串也是无序的。现在,如何生成一个顺序与LinkedHashMap相同的JSON字符串?

【问题讨论】:

如果您在一个简短但完整的程序中向我们展示这个问题,这将真的有所帮助...(代码、预期输出、实际输出。)跨度> 您可以为每个 JSON 对象添加一个元素 order,这样您就可以在接收和解析 JSON 后跟踪字符串排序。 @JonSkeet:我现在已经编辑了这个问题..希望这已经足够清楚了。 嗯。我机器上的输出只是 - 但无论如何它都没有编译开始。请使用您编译并运行的实际代码更新问题... @MrPolywhirl:我已经回滚到修订版 4,因为虽然文本可能不太好,但它至少包含一个(有缺陷的)示例...... 【参考方案1】:

为有序对创建 JsonArray...

JSONArray orderedJson=new JSONArray();
Iterator iterator= ;
    while (iterator.hasNext()) 
        Map.Entry me = (Map.Entry)iteratornext();
        System.out.print(me.getKey() + ": ");
        System.out.println(me.getValue());
        JSONObject tmpJson=new JSONObject ();
        tmpJson.put(me.getKey(),me.getValue());//add key/value to tmpjson
        orderedJson.add(tmpJson);//add tmpjson to orderedJson
    

【讨论】:

但是如何在保持顺序的情况下转换成一个 JSON 字符串呢? 把这个 JSONArray 简单地放入一个 JSONObject 中......例如。 JSONObject jObject=new JSONObject();jObject.put(orderedJson);【参考方案2】:

由于linkedhashmap 参数都是字符串,JSON 没有采用插入顺序。像下面提到的代码一样将第一个参数更改为整数是否可以:

Map<Integer,String > myLinkedHashMap =  new LinkedHashMap<>();
            myLinkedHashMap.put(1,"first");
            myLinkedHashMap.put(2,"second");
            myLinkedHashMap.put(3,"third");

            System.out.println(myLinkedHashMap);
            JSONObject json = new JSONObject(myLinkedHashMap);
            System.out.println(json.toString());

【讨论】:

但是如何在保持顺序的情况下转换成一个 JSON 字符串呢? 我建议的代码是有序维护的。无法理解您的问题。 不,这样不行 - 问题是JSONObject 在幕后使用了一个普通的HashMap,你不能改变它:( @Jon Skeet - 这有什么问题?这个输出错了吗?正在获取上述程序的输出:1=first, 2=second, 3=third "1":"first","2":"second","3":"third" @Subbu:我怀疑这是巧合而不是保证。 (您仍然依赖HashMap 的排序,这是一个非常糟糕的主意。)【参考方案3】:

Gson 如果你的朋友。这会将有序的地图打印成有序的 JSON 字符串。

如果您想保留插入顺序,请使用LinkedHashMap

我使用的是最新版本的Gson(2.8.5),您可以通过本文底部的以下选项进行下载。

import java.util.*;
import com.google.gson.Gson;

public class OrderedJson 
    public static void main(String[] args) 
        // Create a new ordered map.
        Map<String,String> myLinkedHashMap = new LinkedHashMap<String, String>();

        // Add items, in-order, to the map.
        myLinkedHashMap.put("1", "first");
        myLinkedHashMap.put("2", "second");
        myLinkedHashMap.put("3", "third");

        // Instantiate a new Gson instance.
        Gson gson = new Gson();

        // Convert the ordered map into an ordered string.
        String json = gson.toJson(myLinkedHashMap, LinkedHashMap.class);

        // Print ordered string.
        System.out.println(json); // "1":"first","2":"second","3":"third"
    

如果您希望项目始终插入正确的位置,请改用TreeMap

import java.util.*;
import com.google.gson.Gson;

public class OrderedJson 
    public static void main(String[] args) 
        // Create a new ordered map.
        Map<String,String> myTreeHashMap = new TreeMap<String, String>();

        // Add items, in any order, to the map.
        myTreeHashMap.put("3", "third");
        myTreeHashMap.put("1", "first");
        myTreeHashMap.put("2", "second");

        // Instantiate a new Gson instance.
        Gson gson = new Gson();

        // Convert the ordered map into an ordered string.
        String json = gson.toJson(myTreeHashMap, TreeMap.class);

        // Print ordered string.
        System.out.println(json); // "1":"first","2":"second","3":"third"
    

依赖选项

Maven

<dependency>
    <groupId>com.google.code.gson</groupId>
    <artifactId>gson</artifactId>
    <version>2.8.5</version>
</dependency>

分级

compile 'com.google.code.gson:gson:2.8.5'

或者您可以访问Maven Central 了解更多下载选项。

【讨论】:

非常感谢 :) 它有效 :) 也感谢 Maven 依赖项,它帮助了 :) 非常有用。 . . :) 杰克逊不能做到这一点真是太糟糕了。 @goat Jackson 做得很好,看看我的回答。 OP 只是不必要地复杂代码......上面的 GSON 示例也得到了纠正。 这个例子不起作用,因为 GSON 使用 TreeMap,所以正确的例子是做 myLinkedHashMap.put("3", "third");和 myLinkedHashMap.put("1", "one"),所以你会看到 GSON 翻转了结果..【参考方案4】:

要按字母顺序排序,这是 Jackson 2.* 的正确方法:

Map<String, String> myLinkedHashMap = new LinkedHashMap<String, String>();
myLinkedHashMap.put("1", "first");
myLinkedHashMap.put("2", "second");
myLinkedHashMap.put("3", "third");

ObjectMapper mapper = new ObjectMapper();
mapper.configure(MapperFeature.SORT_PROPERTIES_ALPHABETICALLY, true);

try 
    System.out.println(mapper.writeValueAsString(myLinkedHashMap));
    //prints "1":"first","2":"second","3":"third"
 catch (JsonProcessingException e) 
    e.printStackTrace();

【讨论】:

也可以使用TreeMap 进行排序。序列化器使用Map 的遍历顺序。我不太清楚为什么答案声称杰克逊不保留排序:确实如此。【参考方案5】:

JSONObject 暗示使用org.json 库。不要那样做;它是旧原型,并且有更好的选择,例如 Jackson 和 GSON。 对于 Jackson,您只需使用:

String json = new ObjectMapper().writeValueAsString(myLinkedHashMap);

并以您的 Map 使用的任何遍历顺序获取带有条目的 JSON 字符串。

【讨论】:

【参考方案6】:

我遇到了同样的问题。我必须使用这个特定的库,并且第一个元素必须带有键“$type”。 这是我的决定:

import org.json.JSONObject;

import java.util.HashMap;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Set;


class TypedJSONObject extends JSONObject 

    private static final String KEY = "$type";

    @Override
    public Set<Map.Entry<String, Object>> entrySet() 
        Set<Map.Entry<String, Object>> superSet = super.entrySet();
        Set<Map.Entry<String, Object>> returnSet = new LinkedHashSet<>();

        HashMap<String, Object> map = new HashMap<>();
        for (Map.Entry<String, Object> entry : superSet) 
            if (entry.getKey().equals(KEY)) 
                map.put(KEY, entry.getValue());
                break;
            
        
        Set<Map.Entry<String, Object>> entries = map.entrySet();

        returnSet.addAll(entries);
        returnSet.addAll(superSet);

        return returnSet;
    

如你所见,我重写了方法 entrySet,它在方法 toString 中使用

【讨论】:

【参考方案7】:

我创建了一个 hacky 解决方案,不应该用于生产,我只是用它来手动生成 json 文件

import java.lang.reflect.Field;
import java.util.LinkedHashMap;

import org.json.JSONObject;

public class SortedJSONObject extends JSONObject 

    public SortedJSONObject() 
        super();
        try 
            Field mapField = JSONObject.class.getDeclaredField("map");
            mapField.setAccessible(true);
            mapField.set(this, new LinkedHashMap());
            mapField.setAccessible(false);
         catch (Exception e) 
            throw new RuntimeException(e);
        
    


【讨论】:

以上是关于从 LinkedHashMap 构建有序 JSON 字符串的主要内容,如果未能解决你的问题,请参考以下文章

从 LinkedHashMap 转换为 Json 字符串

JSONObject如何有序排列

linkedHashMap转对象后元素乱序怎么解决?

LinkedHashMap是如何实现有序的?

Map的有序和无序实现类,与Map的排序

HashMap,LinkedHashMap,TreeMap的有序性