从LinkedHashMap构建有序的JSON字符串

13

我需要按照插入顺序存储键值对,所以我选择使用LinkedHashMap而不是HashMap。但我需要将LinkedHashMap转换为JSON字符串,并在字符串中保留LinkedHashMap中的顺序。

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

  1. 首先将LinkedHashMap转换为JSON。
  2. 然后将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字符串?


1
如果您能展示一个简短但完整的程序来演示问题,那将非常有帮助...(包括代码、期望输出和实际输出)。 - Jon Skeet
您可以向每个JSON对象添加一个元素order,以便在接收和解析JSON后能够跟踪字符串顺序。 - Bruno Toffolo
@JonSkeet:我现在已经编辑了这个问题...希望这样就足够清楚了。 - Karthic Rao
嗯,我的机器上的输出只是{} - 但是它本来就没有编译成功。请更新问题,提供您已编译和运行的实际代码... - Jon Skeet
@MrPolywhirl:我已经回滚到第4个版本,因为尽管文本可能不太好,但至少包含了一个(有缺陷的)示例... - Jon Skeet
显示剩余7条评论
7个回答

18

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>

Gradle

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

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


4
非常感谢 :) 它有效了 :) 还要感谢Maven依赖,它帮了大忙 :) - Karthic Rao
2
非常有用... :) - msoa
1
Jackson不能做这件事,真是太糟糕了。 - goat
1
@goat Jackson做得很好,可以看看我的答案。OP只是不必要地使代码复杂化了...上面的GSON示例也纠正了这一点。 - StaxMan
这个例子不起作用是因为GSON使用了TreeMap,所以正确的例子应该是myLinkedHashMap.put("3", "third");和myLinkedHashMap.put("1", "one"),这样你会看到GSON翻转了结果。 - Rafael Sanches
@RafaelSanches,我更新了响应以包括TreeMap,但请不要因为我正确回答而对我进行负评;关于使用LinkedHashMap的问题。 - Mr. Polywhirl

6

JSONObject 暗示了使用 org.json 库。不要这样做;它是旧的原型,有更好的选择,比如 Jackson 和 GSON。 使用 Jackson 只需使用以下代码:

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

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


1
由于LinkedHashMap参数都是字符串,因此JSON不会考虑插入顺序。是否可以像下面的代码一样将第一个参数更改为Integer:
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字符串? - Karthic Rao
我的建议代码是有序维护的。无法理解你的问题。 - Subbu
1
不行,这样做不了 - 问题在于 JSONObject 在后台使用的是普通的 HashMap,你无法改变它 :( - Jon Skeet
@Jon Skeet - 这里有什么问题?这个输出是错误的吗?我得到了上面程序的输出:{1=first, 2=second, 3=third} {"1":"first","2":"second","3":"third"} - Subbu
@Subbu:我认为这只是巧合而不是保证。 (您仍然依赖于“HashMap”的排序,这是一个非常糟糕的想法。) - Jon Skeet
Jon Skeet先生是正确的,HashMap的顺序没有保证,它只是巧合地变得正确。我想要的是在具有大量键/值对的HashMap上保证排序。 - Karthic Rao

0

要按字母顺序排序,这里是使用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的遍历顺序。我不太确定为什么有些答案声称Jackson不能保留顺序:它确实可以。 - StaxMan

0

我创建了一个不太正规的解决方案,不应该用于生产环境,我只是用它手动生成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);
        }
    }

}

0

我遇到了同样的问题。我必须使用这个特定的库,而第一个元素必须带有键"$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 方法中被使用。


-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字符串? - Karthic Rao
将这个JSONArray简单地放入一个JSONObject中,例如:JSONObject jObject = new JSONObject(); jObject.put(orderedJson); - Angad Tiwari

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