如何维护JSONObject的顺序

时间:2012-03-28 13:58:42

标签: java json

我正在使用JSONObject来删除JSON字符串中不需要的certin属性:

JSONObject jsonObject = new JSONObject(jsonString);
jsonObject.remove("owner");
jsonString = jsonObject.toString();

它的工作正常但问题是JSONObject是“一个无序的名称/值对集合”,我想保持String在进行JSONObject操作之前的原始顺序。

知道怎么做吗?

12 个答案:

答案 0 :(得分:3)

你做不到。

这就是为什么我们将其称为无序的名称/值对集合

为什么你需要这样做,我不确定。但是如果你想要订购,你将不得不使用一个json数组。

答案 1 :(得分:2)

我最近遇到了同样的问题,只是将我们所有的测试(希望JSON属性的顺序相同)转换到另一个JSON库:

        <dependency>
            <groupId>org.codehaus.jettison</groupId>
            <artifactId>jettison</artifactId>
            <version>1.3.5</version>
        </dependency>

在内部,它使用LinkedHashMap,它维护属性的顺序。这个库在功能上等同于json.org,所以我没有看到任何理由不使用它,至少对于测试而言。

答案 2 :(得分:2)

尝试

JSONObject jsonObject = new JSONObject(jsonString) {
    /**
     * changes the value of JSONObject.map to a LinkedHashMap in order to maintain
     * order of keys.
     */
    @Override
    public JSONObject put(String key, Object value) throws JSONException {
        try {
            Field map = JSONObject.class.getDeclaredField("map");
            map.setAccessible(true);
            Object mapValue = map.get(this);
            if (!(mapValue instanceof LinkedHashMap)) {
                map.set(this, new LinkedHashMap<>());
            }
        } catch (NoSuchFieldException | IllegalAccessException e) {
            throw new RuntimeException(e);
        }
        return super.put(key, value);
    }
};
jsonObject.remove("owner");
jsonString=jsonObject.toString();

答案 3 :(得分:1)

如果您可以编辑服务器repose,则将其更改为JSON对象数组。

JSON:

[
{PropertyName:"Date of Issue:",PropertyValue:"3/21/2011"},
PropertyName:"Report No:",PropertyValue:"2131196186"},{PropertyName:"Weight:",PropertyValue:"1.00"},
{PropertyName:"Report Type:",PropertyValue:"DG"}
]

我在客户端(Android)使用JSONArray处理它:

String tempresult="[{PropertyName:"Date of Issue:",PropertyValue:"3/21/2011"},PropertyName:"Report No:",PropertyValue:"2131196186"},PropertyName:"Weight:",PropertyValue:"1.00"},{PropertyName:"Report Type:",PropertyValue:"DG"}]"

JSONArray array = new JSONArray(tempresult);
             for (int i = 0; i < array.length(); i++) 
             {
                 String key = array.getJSONObject(i).getString("PropertyName"); 
                 String value = array.getJSONObject(i).getString("PropertyValue");
                 rtnObject.put(key.trim(),value.trim()); //rtnObject is LinkedHashMap but can be any other object which can keep order.


         }

答案 4 :(得分:0)

这不容易,主要思想是使用LinkedHashMap,或者传入构造函数(JSONObject(Map map)),或者修改字节码来处理String参数(JSONObject(String source)),这是主要的用例。我在oson中找到了一个解决方案:

    public static JSONObject getJSONObject(String source) {
    try {
        int lineNumberToReplace = 157;

        ClassPool classPool = ClassPool.getDefault();
        CtClass ctClass = classPool.get("org.json.JSONObject");

        if (ctClass.isFrozen() || ctClass.isModified()) {
            if (source == null) {
                return new JSONObject();
            } else {
                return new JSONObject(source);
            }
        }

        ctClass.stopPruning(true);
        CtConstructor declaredConstructor = ctClass.getDeclaredConstructor(new CtClass[] {}); 

        CodeAttribute codeAttribute = declaredConstructor.getMethodInfo().getCodeAttribute();

        LineNumberAttribute lineNumberAttribute = (LineNumberAttribute)codeAttribute.getAttribute(LineNumberAttribute.tag);

        // Index in bytecode array where the instruction starts
        int startPc = lineNumberAttribute.toStartPc(lineNumberToReplace);

        // Index in the bytecode array where the following instruction starts
        int endPc = lineNumberAttribute.toStartPc(lineNumberToReplace+1);

        // Let's now get the bytecode array
        byte[] code = codeAttribute.getCode();
        for (int i = startPc; i < endPc; i++) {
          // change byte to a no operation code
           code[i] = CodeAttribute.NOP;
        }

        declaredConstructor.insertAt(lineNumberToReplace, true, "$0.map = new java.util.LinkedHashMap();");

        ctClass.writeFile();

        if (source == null) {
            return (JSONObject) ctClass.toClass().getConstructor().newInstance();
        } else {
            return (JSONObject) ctClass.toClass().getConstructor(String.class).newInstance(source);
        }

    } catch (Exception e) {
        //e.printStackTrace();
    }

    if (source == null) {
        return new JSONObject();
    } else {
        return new JSONObject(source);
    }
}

需要包含使用mvn

的jar文件
<dependency>
    <groupId>javassist</groupId>
    <artifactId>javassist</artifactId>
    <version>3.12.1.GA</version>
</dependency>

答案 5 :(得分:0)

在Android 20中,JSONObject保留顺序,因为它使用LinkedHashMap存储namevaluepairs。 Android 19及更低版本使用HashMap存储namevaluepairs。因此,Android 19及以下版本不保留订单。如果您使用20或更高版本,请不要担心,JSONObject将保留订单。或者,改为使用JSONArray。

答案 6 :(得分:0)

在JDK 8及更高版本中,我们可以使用JDK 8支持的 nashorn 引擎来实现。 Java 8支持使用js引擎来评估:

String content = ..json content...  
String name = "test";  
String result = (String) engine.eval("var json = JSON.stringify("+content+");"
                                + "var jsResult = JSON.parse(json);"
                                + "jsResult.name = \"" + name + "\";"
                                + "jsResult.version = \"1.0\";"
                                + "JSON.stringify( jsResult );"
                            );

答案 7 :(得分:0)

您可以使用com.google.gson提供的JsonObject,它与org.json的JSONObject几乎相同,但功能有所不同。 要将String转换为Json对象并保持顺序,可以使用:

Gson gson = new Gson();
JsonObject jsonObject = gson.fromJson(<Json String>, JsonObject.class);

例如:-

String jsonString = "your json String";

JsonObject jsonObject = gson.fromJson(jsonString, JsonObject.class);

它只是维护String中JsonObject的顺序。

答案 8 :(得分:0)

您可以使用Jsckson library来保持Json键的顺序。 它内部使用LinkedHashMap(已排序)。

import com.fasterxml.jackson.core.JsonToken;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;

用于删除字段的代码,如果需要,可以读取已删除的JsonToken本身。

  String jsonString = "{\"name\":\"abc\",\"address\":\"add\",\"data\":[\"some 1\",\"some 2\",\"some3 3\"],\"age\":12,\"position\":8810.21}";
  ObjectMapper mapper = new ObjectMapper();
  JsonNode node = mapper.readTree(jsonString);
  System.out.println("In original order:"+node.toString());
  JsonToken removedToken = ((ObjectNode) node).remove("address").asToken();
  System.out.println("Aft removal order:"+node.toString());

ObjectNode实现使用LinkedHashMap,它维护插入顺序:

 public ObjectNode(JsonNodeFactory nc) {
   super(nc);
   _children = new LinkedHashMap<String, JsonNode>();
 }

答案 9 :(得分:0)

继续 JSONObject类 HashMap()更改为 LinkedHashMap()

 /**
     * Construct an empty JSONObject.
     */
    public JSONObject() {
        this.map = new LinkedHashMap();
    }

哈希类的哈希表。 Cette classe使用un liste doubleetchaînée竞争者吹捧了桌子餐桌上的ententes lesentréesde la tablehachée,dans l'ordreoùlesclésontétéinséréesdans la table:ceci permet d'obtenir un“ non chaotique” sur lesclés。 >

答案 10 :(得分:0)

我能够在类路径覆盖的帮助下做到这一点。

  1. 创建了程序包package org.json.simple,该程序包与jar和名为JSONObject的类中的程序包相同。
    1. 从jar中获取现有代码,并通过扩展 LinkedHashmap 而不是 Hashmap
    2. 来更新类。

通过执行这两个步骤,它将保持顺序,因为从步骤1中创建的新包中选择`JSONObject的偏好要比jar更高。

答案 11 :(得分:0)

我通过执行以下操作来完成它:

JSONObject(json).put(key, ObjectMapper().writeValueAsString(ObjectMapper().readValue(string, whatever::class)))

因此本质上我将字符串反序列化为有序类,然后再次对其进行序列化。但是后来我还必须格式化该字符串以删除转义符。

.replace("\\\"", "\"").replace("\"{", "{").replace("}\"", "}")

如果您不希望使用null,则可能还必须替换null项目。