Java中将XML转换为JSON元素的顺序颠倒了。

11
我正在使用Java中的JSON将XML转换为JSON。 我遇到的问题是此实现会反转所有子元素。

当我传递此XML时:

<Person><Child1>a</Child1><Child2>b</Child2></Person>

最终我会得到一个子元素被倒置的JSON:

{"Person":{"Child2":"b", "Child1":"a"}}

我的Java代码:

JSONObject jsonObject= XML.toJSONObject("<Person><Child1>a</Child1><Child2>b</Child2></Person>");
String myJSONString = jsonObject.toString(4);

如何将元素顺序保留在JSON中(类似于XML)?


4
我的建议是不要担心。在XML或JSON元素中,没有暗示任何顺序的定义。 - mikea
3
在XML中,元素的顺序很重要 - 任何具有一系列元素(在XML模式中指定)的元素都会检查元素的顺序。因此,这是很重要的...(可能不是在JSON中,但至少在XML中是这样的)。 - Philipp
重点是,即使您将其转换为JSON,任何随后处理JSON字符串的JSON工具都可能打乱顺序。获取JSON“对象”中的元素没有必要按照给定的顺序排列,因为它们不会保持原样。 - Hot Licks
JSON列表中元素的顺序是否保持不变? - Hot Licks
当您尝试从创建的JSON生成XML时会发生什么?您是否得到与原始文档完全相同的XML文档? - Dinesh
显示剩余7条评论
8个回答

12

我的问题是如何在保持顺序的情况下转换为JSON?

使用当前官方的 JSONObject 是不可能的。该API非常明确:

JSONObject 是无序的名称/值对集合。

但是,你可以尝试一个快速解决方案。通过查看 JSONObject 源代码,我发现它内部使用了 HashMap,并且 HashMap 不保留任何顺序。

public JSONObject() { this.map = new HashMap<String, Object>(); }

你有两个选择:

  1. 修改当前 JSONObject 源代码,以便使用 LinkedHashMap 为映射初始化。 LinkedHashMap 是 Map 接口的实现,具有可预测的迭代顺序:

    public JSONObject() {
          this.map = new LinkedHashMap<String, Object>();
    }
    
  2. 创建一个继承自JSONObject的自定义类,内部使用LinkedHashMap。注意,你仍然需要对JSONObject进行一些更改。

  3. public class JSONObject {
    
        //private final Map<String,Object> map; // current approach
        //you have to remove final modifier and either add a getter or make it protected. I'll choose the change modifier to protected in this case.
        protected Map<String,Object> map;
    
    }
    
    public class JSONObjectOrdered extends JSONObject {
        public JSONObjectOrdered(){
            this.map = new LinkedHashMap <String, Object>();
        } 
    }
    

你无法看到map字段,因为它是私有的。只有受保护的公共的修饰符才会在这里放置。 - Sergey Shustikov
1
阅读map声明旁边的注释。我说了你必须做什么才能应用第二种替代方案。 - Daniel
很遗憾,这并没有帮助。XML.toJSONObject()函数返回按字母顺序排序的元素。我使用了LinkedHashMap,并且还更改了keySet()函数。似乎这个方法根本没有被调用。 - Philipp
@FiveO 刚刚测试了一下:"<Person><Child1>benjamin</Child1><Child2>ana</Child2></Person>"它确实保持了顺序。XML.toJSONObject通过逐个解析给定的XML字符串节点来构建一个JSONObject,完全不应用任何词汇顺序。您可以在这里进行检查。 - Daniel
1
@Daniel,我想在我的任务中使用你的解决方案,但是我可以问一下你是如何在项目中使用JSON源代码的吗?当我将jar文件添加到我的项目中时,由于它处于只读格式,我无法编辑源代码。 - Nobi

1
作为一个无序的名称/值对集合,因此没有选择,您必须使用JSONArray。
这是我的解决方案,修改XML类,特别是方法parse,以便返回JSONArray来存储子节点。
我修改后的类:XML.java XML输入
<Person name="test"><Child1>a</Child1><Child2>b</Child2><Child3></Child3></Person>

使用方法:

JSONObject jsonObject= XML.toJSONObject("<Person name=\"test\"><Child1>a</Child1><Child2>b</Child2><Child3></Child3></Person>");
System.out.println(jsonObject);

输出:

{"Person":{"CHILDREN":[{"Child1":"a"},{"Child2":"b"},{"Child3":""}],"name":"test"}}

结论

孩子的顺序被保持。当然,这个想法可以得到改进,这只是一个关于可以做什么的 POC,修改解析器。


0

JSON对象没有特定的顺序。当然,您可以更改序列化实现以保持顺序,但不能保证在反序列化后仍保留该顺序。事实上,大多数JSON库甚至都没有API来检测原始JSON文本被解析的顺序。当使用对象时,您不应关心排序。

如果您确实关心顺序,请使用JSON数组。

{"Person":[{"Child1":"a"},{"Child2":"b"}]}

1
看起来有人给所有答案点了踩,但我真的很想知道其他踩的原因。在JSON规范中,不可能有一个有序对象。数组是唯一有序的数据结构。如果你必须实现自己的序列化器和解析器来进行编组,那么你就不再使用标准的JSON了。唯一的其他选择是将排序移到应用程序级别,例如使用可排序的键或添加额外的属性进行排序。 - kapex

0

如果你非常执着于按照自己想要的方式对输出进行排序,你可以尝试重写这个方法。

 toString() 

0
  1. 您可以从http://www.json.org/java/下载源代码,并使用TreeMap而不是HashMap修改JSONObject.java。

  2. 您还可以覆盖JSONObject.java中的方法

    public Iterator<String> keys() {
      return this.keySet().iterator();
    }
    

    确保迭代器是排序后的键之一。


0
如果您想要使用 Jackson 进行JSON序列化/反序列化,那么您可以在您的类上面加上一个@JsonPropertyOrder() 注解。
@JsonPropertyOrder({"Child1", "Child2"})
public class Person {

    @JsonProperty("Child1")
    public String child1;

    @JsonProperty("Child2")
    public String child2;
}

我没有Java类表示。输入的XML结构必须是动态的,可以改变。 - Philipp

0

JSONObject API 不能保证元素的顺序。

解决这个问题的好方法是使用JSONArray,在JSONArray中,您插入元素的顺序将被保存。

因此,在您的情况下,您将为每个人拥有一个“chides”数组。您可能需要更改XML文件或手动将XML解析为您的格式中的json(使用JSONArray而不是现在使用的内容)。


-1

你可以在修改时保持传入数据的顺序。

private final Map<String, Object> nameValuePairs;

/**
 * Creates a {@code JSONObject} with no name/value mappings.
 */
public JSONObject() {
    nameValuePairs = new HashMap<String, Object>();
}

private final Map<String, Object> nameValuePairs;

/**
 * Creates a {@code JSONObject} with no name/value mappings.
 */
public JSONObject() {
    nameValuePairs = new LinkedHashMap<String, Object>();
}

因为相比于HashMapLinkedHashMap具有可预测的迭代顺序。

LinkedHashMap:Map接口的哈希表和链表实现,具有可预测的迭代顺序。

因此,这是解决您问题最有效的方法。

另外,您也可以使用fork从自定义库中获取。

https://github.com/SergeyShustikov/JSON-java


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