简体   繁体   English

如何维护 JSONObject 的顺序

[英]How to maintain the order of a JSONObject

I am using a JSONObject in order to remove a certin attribute I don't need in a JSON String:我正在使用JSONObject来删除我在 JSON 字符串中不需要的 certin 属性:

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

It works ok however the problem is that the JSONObject is "an unordered collection of name/value pairs" and I want to maintain the original order the String had before it went through the JSONObject manipulation.它工作正常,但问题是 JSONObject 是“名称/值对的无序集合”,我想保持 String 在通过 JSONObject 操作之前的原始顺序。

Any idea how to do this?知道如何做到这一点吗?

I have faced the same problem recently and just transitioned all our tests (which expect JSON attributes to be in the same order) to another JSON library:我最近遇到了同样的问题,只是将我们所有的测试(期望 JSON 属性的顺序相同)转换到另一个 JSON 库:

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

Internally it uses a LinkedHashMap , which maintains the order of attributes.它在内部使用LinkedHashMap ,它维护属性的顺序。 This library is functionally equivalent to the json.org library, so I don't see any reason why not use it instead, at least for tests.这个库在功能上等同于json.org库,所以我看不出有什么理由不使用它,至少在测试中是这样。

try this试试这个

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();

You can't.你不能。

That is why we call it an unordered collection of name/value pairs .这就是为什么我们称其为名称/值对的无序集合

Why you would need to do this, I'm not sure.为什么你需要这样做,我不确定。 But if you want ordering, you'll have to use a json array.但是,如果您想订购,则必须使用 json 数组。

Go on JSONObject class Change from HashMap() to LinkedHashMap()继续JSONObject 类HashMap()更改为LinkedHashMap()

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

The LinkedHashMap class extends the Hashmap class. LinkedHashMap 类扩展了 Hashmap 类。 This class uses a doubly linked list containing all the entries of the hashed table, in the order in which the keys were inserted in the table: this allows the keys to be "ordered".此类使用包含散列表中所有条目的双向链表,按照键在表中插入的顺序:这允许键“排序”。

If you can edit the server repose then change it to array of JSON objects.如果您可以编辑服务器配置,则将其更改为 JSON 对象数组。

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"}
]

And I handled it with JSONArray in client side (Android):我在客户端(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.


         }

You can go for the JsonObject provided by the com.google.gson it is nearly the same with the JSONObject by org.json but some different functions.您可以使用 com.google.gson 提供的 JsonObject,它与 org.json 提供的 JSONObject 几乎相同,但有一些不同的功能。 For converting String to Json object and also maintains the order you can use:用于将 String 转换为 Json 对象并维护您可以使用的顺序:

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

For eg:-例如:-

String jsonString = "your json String";

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

It just maintains the order of the JsonObject from the String.它只是维护 JsonObject 与 String 的顺序。

You can use Jsckson library in case to maintain the order of Json keys.您可以使用Jsckson 库来维护 Json 键的顺序。 It internally uses LinkedHashMap ( ordered ).它在内部使用 LinkedHashMap (ordered)。

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;

The code to remove a field, the removed JsonToken could itself be read if required.删除字段的代码,如果需要,可以读取删除的 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 implementation uses a LinkedHashMap, which maintains the insertion order: ObjectNode 实现使用 LinkedHashMap,它维护插入顺序:

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

This is not easy, the main idea is to use LinkedHashMap, either pass in to the constructor (JSONObject(Map map)), or modify bytecode to handle the String parameter (JSONObject(String source)), which is the main use case.这并不容易,主要思想是使用LinkedHashMap,要么传入构造函数(JSONObject(Map map)),要么修改字节码来处理String参数(JSONObject(String source)),这是主要用例。 I got a solution in oson :我在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);
    }
}

need to include jar file from using mvn需要使用 mvn 包含 jar 文件

<dependency>
    <groupId>javassist</groupId>
    <artifactId>javassist</artifactId>
    <version>3.12.1.GA</version>
</dependency>

From Android 20, JSONObject preserves the order as it uses LinkedHashMap to store namevaluepairs.从 Android 20 开始,JSONObject 保留顺序,因为它使用 LinkedHashMap 存储名称值对。 Android 19 and below uses HashMap to store namevaluepairs. Android 19 及以下版本使用 HashMap 来存储名称值对。 So, Android 19 and below doesn't preserve the order.因此,Android 19 及以下版本不保留顺序。 If you are using 20 or above, don't worry, JSONObject will preserve the order.如果您使用的是 20 或以上,请不要担心,JSONObject 会保留顺序。 Or else, use JSONArray instead.否则,请改用 JSONArray。

In JDK 8 and above, We can do it by using nashorn engine, supported in JDK 8. Java 8 support to use js engine to evaluate:在JDK 8及以上,我们可以使用nashorn引擎,JDK 8支持。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 );"
                            );

I was able to do this with help of classpath overriding.在类路径覆盖的帮助下,我能够做到这一点。

  1. created package package org.json.simple which is same as in jar and class named as JSONObject .创建包package org.json.simple与 jar 和名为JSONObject类相同。
    1. Took existing code from jar and updated the class by extending LinkedHashmap instead of Hashmap从 jar 中获取现有代码并通过扩展LinkedHashmap而不是Hashmap来更新类

by doing these 2 steps it will maintain the order, because preference of picking `JSONObject will be higher to pick from the new package created in step 1 than the jar.通过执行这 2 个步骤,它将保持顺序,因为从步骤 1 中创建的新包中选择 `JSONObject 的偏好将高于 jar。

I accomplished it by doing a:我通过执行以下操作来完成它:

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

So essentially I deserialize a string to an ordered class, then I serialize it again.所以基本上我将一个字符串反序列化为一个有序的类,然后我再次序列化它。 But then I also had to format that string afterwards to remove escapes.但是后来我还必须格式化该字符串以删除转义符。

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

You may also have to replace null items as well if you don't want nulls.如果您不想要空值,您可能还必须替换空值项目。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM