【问题标题】:How to maintain the order of a JSONObject如何维护 JSONObject 的顺序
【发布时间】:2012-04-12 03:17:24
【问题描述】:

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

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

它工作正常,但问题是 JSONObject 是“名称/值对的无序集合”,我想保持字符串在通过 JSONObject 操作之前的原始顺序。

知道怎么做吗?

【问题讨论】:

标签: java json


【解决方案1】:

你不能。

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

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

【讨论】:

  • 我只需要它,在这里很难解释整个系统:) JSON 数组似乎是正确的方法,谢谢
  • 一个问题:JSONArray 有我想使用的 remove 方法,但我需要为其提供索引,但我找不到返回属性索引的方法大批。除了手动循环数组之外还有什么想法吗?
  • JSON 数组中属性的索引是它在数组中的位置。试试jsonObject.getJSONArray("name").remove(12),其中 12 是数组中的索引。
  • 是的,但我需要先弄清楚哪个索引。无论如何,我已经用 for 循环完成了它,但现在遇到了一个不同的问题。虽然我能够从我的 JSON 字符串创建一个 JSONObject,但它不喜欢它是一个 JSONArray,获取一个 JSONArray 文本必须以字符 1 处的 '[' 开始
  • 我认为杰克逊在这里可能是一个更好的解决方案
【解决方案2】:

如果您可以编辑服务器休息,则将其更改为 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.


         }

【讨论】:

    【解决方案3】:

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

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

    在内部它使用LinkedHashMap,它维护属性的顺序。这个库在功能上等同于 json.org 库,所以我看不出有什么理由不使用它,至少在测试中是这样。

    【讨论】:

    • 我很高兴你把这件事告诉了@anydoby。在序列化对象时测试 Jackson 的属性顺序的需要促使我搜索这个问题。现在,我打算只使用忽略空格的简单字符串比较;但理想情况下,我想要一个 JSON 对象,它可以保持给定元素的顺序,即使我们知道顺序应该是任意的。
    【解决方案4】:

    这个不容易,主要思路是使用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>
    

    【讨论】:

    • 只是希望你不要在生产中使用这个解决方案。
    • 不幸的是,这是更改私有 final 字段的两种方法之一,另一种是创建一个全新的类,替换 JSONObject,并将 HashMap 更改为 LinkedHashMap。所有其他解决方案,要么不够通用,要么根本不起作用,例如反射,扩展以创建子类...
    • 与其添加一个依赖来修改字节码和执行变通方法,为什么不添加一个提供开箱即用功能的 JSON 解析器的依赖呢?例如,可以通过 Jackson 来实现。
    【解决方案5】:

    从 Android 20 开始,JSONObject 在使用 LinkedHashMap 存储名称值对时保留了顺序。 Android 19 及更低版本使用 HashMap 来存储名称值对。因此,Android 19 及更低版本不保留顺序。如果您使用的是 20 或以上,请不要担心,JSONObject 会保留顺序。否则,请改用 JSONArray。

    【讨论】:

      【解决方案6】:

      在 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】:

        您可以使用 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);
        

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

        【讨论】:

          【解决方案8】:

          你可以使用Jsckson library来维护Json键的顺序。 它在内部使用 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;
          

          移除字段的代码,如果需要,移除的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】:

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

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

            LinkedHashMap 类扩展了 Hashmap 类。此类使用包含散列表的所有条目的双向链表,按照键在表中插入的顺序:这允许键被“排序”。

            【讨论】:

            • 请用英文回答
            • 应该如何做到这一点? JSONObject 来自第三方库。
            【解决方案10】:

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

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

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

            【讨论】:

              【解决方案11】:

              试试这个

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

              【讨论】:

              • 使用构造函数可能会更好 public class JSONObjectOrder extends JSONObject { public JSONObjectOrder() { super();尝试 { 字段映射 = JSONObject.class.getDeclaredField("map"); map.setAccessible(true);对象 mapValue = map.get(this); if(!(mapValue instanceof LinkedHashMap)) { map.set(this, new LinkedHashMap<string object>()); } } catch(NoSuchFieldException | IllegalAccessException e) { throw new RuntimeException(e); } } } </string>
              • @David 您的建议并未涵盖 JSONObject 的所有 3 个构造函数。带map参数的构造函数的问题是在填充之前不能将字段map改成LinkedHashMap
              【解决方案12】:

              我做了一个:

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

              所以基本上我将一个字符串反序列化为一个有序类,然后我再次序列化它。但后来我还必须格式化该字符串以删除转义。

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

              如果您不想要 null,您可能还必须替换 null 项。

              【讨论】:

                猜你喜欢
                • 2015-09-12
                • 2014-08-10
                • 1970-01-01
                • 2021-12-26
                • 1970-01-01
                • 1970-01-01
                • 2013-11-02
                • 2011-02-27
                • 1970-01-01
                相关资源
                最近更新 更多