11

I'm using the JSON in Java for the transformation of XML to JSON. I have the problem that this implementation is inverting all child elements.

When I pass this XML:

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

I will end up with a JSON having the childs inverted:

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

My Java code:

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

How to transform to JSON with keeping the order of the elements (like in XML)?

Philipp
  • 4,645
  • 3
  • 47
  • 80
  • 4
    My advice is not to worry. There is no implied order defined in XML or JSON elements. – mikea Sep 25 '14 at 08:57
  • 3
    No, in XML the order of the elements are important - any element having a sequence of elements (specified in the XML Schema) is checking for the order of the elements. So it is important... (maybe not in JSON, but at least in the XML it does). – Philipp Oct 15 '14 at 12:06
  • The order of elements in a JSON "object" is not defined. Even if you somehow get the order you want, it's not guaranteed to remain in that order as the JSON is processed. – Hot Licks Oct 15 '14 at 12:12
  • (Elements in a JSON "array", however, are ordered.) – Hot Licks Oct 15 '14 at 12:13
  • From the [official page](http://www.json.org/java/) : *A JSONObject is an **unordered** collection of name/value pairs.* – Daniel Oct 15 '14 at 12:17
  • Ok good that's now understood. But I still have the problem that I need to re-order all elements when I transform it back to XML! – Philipp Oct 15 '14 at 12:19
  • So my question. How to transform to JSON with keeping the order? – Philipp Oct 15 '14 at 12:19
  • When converting to JSON, you lost the ordering information, it can't be guessed after that – yunandtidus Oct 15 '14 at 12:56
  • The point is that even after you convert to JSON the order may be jumbled by any JSON tool that subsequently processes the JSON string. There's no reason for getting the elements in a JSON "object" to be in a given order, because they won't stay that way. – Hot Licks Oct 15 '14 at 15:30
  • Duplicate of http://stackoverflow.com/questions/7214293/is-the-order-of-elements-in-a-json-list-maintained – Hot Licks Oct 15 '14 at 15:32
  • What happens when you try to generate the XML from the created JSON? Do you get an XML document identical to the original? – Dinesh Oct 21 '14 at 21:20
  • I did the change of the LinkedHashMap, but no success so far. Still the same issue.... I do some more tests and let you know in the few hours... – Philipp Oct 22 '14 at 07:40

8 Answers8

12

So my question. How to transform to JSON with keeping the order?

With the current official JSONObject, this is not possible. The API makes it very clear:

A JSONObject is an unordered collection of name/value pairs.

But, there might be a quick workaround for your problem. As from what I've investigated in the JSONObject source code, you can see that it uses a HashMap internally, and as you know HashMap doesn't keep any order.

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

You have 2 alternatives:

  1. Modify the current JSONObject source code so that the map is initialized with a LinkedHashMap. A LinkedHashMap is an implementation of the Map interface, with predictable iteration order:

    public JSONObject() {
          this.map = new LinkedHashMap<String, Object>();
    }
    
  2. Make your own custom class which extends JSONObject but uses a LinkedHashMap internally. Notice that you still have to make some changes in JSONObject.

    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>();
        } 
    }
    
Daniel
  • 1,861
  • 1
  • 15
  • 23
  • you can't see a `map` field, because is **private**. Only **protected** or **public** modificators will be placed here. – Sergey Shustikov Oct 15 '14 at 12:58
  • 1
    Read the comment next to `map` declaration. I said what you **have to do** in order to apply alternative no 2. – Daniel Oct 15 '14 at 12:59
  • Unfortunately this does not help. The XML.toJSONObject() function returns the element sorted alphabetically. I use the LinkedHashMap and changed also the keySet() function. Seems not to be called by this method at all. – Philipp Oct 22 '14 at 08:08
  • @FiveO just tested for: `"benjaminana"` and **IT DOES** keep order. The `XML.toJSONObject` builds a `JSONObject` by parsing the given XML string node after node, not applying any lexicographical order at all. You can check [here](https://github.com/douglascrockford/JSON-java/blob/master/XML.java) – Daniel Oct 22 '14 at 08:20
  • 1
    @Daniel, I want to use your solution in my task, but may I ask how you were able to use the JSON source code in your project? when I add the jar file to mine, i am unable to edit the source code as it is in readonly format. – Nobi Sep 25 '18 at 19:21
1

As JSONObject is an unordered collection of name/value pairs, no choice, you have to use a JSONArray.

Here is my solution, modify the XML class, particularly the method parse, in order to return JSONArray for storing child nodes.

My modified class : XML.java

XML input

<Person name="test"><Child1>a</Child1><Child2>b</Child2><Child3></Child3></Person>

Usage :

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

Out :

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

Conclusion

The children order is kept. Off course this idea can be improved, it's just a POC regarding what can be done, modifying the parser.

ToYonos
  • 16,469
  • 2
  • 54
  • 70
0

JSON objects don't have a specific order. You can of course change the serialization implementation to keep an order but there is no guarantee that it is also kept after deserialization. In fact, most JSON libraries won't even have an API to detect in which order the original JSON text was parsed. You shouldn't care about ordering when using objects.

If you do care about the order though, use a JSON array.

{"Person":[{"Child1":"a"},{"Child2":"b"}]}
kapex
  • 28,903
  • 6
  • 107
  • 121
  • 1
    Looks like someone downvoted all answers, but I really like to know the reason for the other downvote. It's simply not possible to have an ordered object within the JSON specification. Arrays are the only ordered data structures. If you have to implement your own serializer and parsers to do the marshaling yourself, you are not using standard JSON any more. The only other option would be to move the ordering to the application level, by for example using sortable keys or adding additional attributes for ordering. – kapex Oct 17 '14 at 09:30
0

The JSONObject API dose not guarantee the elements order A nice solution to this issue can be using JSONArray, in JSONArray the order you insert the elements is saved.

So, in your case you will have an array of "chides" for each person. you would probably will need to change the XML file or manually parse the XML into the json in your format (the JSONArray instead of what you are using now)

0

If you are hell bent on getting the output ordered the way you want it you could always try overriding the method

 toString() 
Jacques Ramsden
  • 801
  • 5
  • 20
0
  1. You can download the source code from http://www.json.org/java/ and modify JSONObject.java using TreeMap instead of HashMap.

  2. You also can override method in JSONObject.java

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

    Make sure the Iterator is the one of the sorted keys.

Neal
  • 1
  • 1
0

If you would use Jackson for JSON serialization / deserialization you could simply put a @JsonPropertyOrder() annotation on top of your class.

@JsonPropertyOrder({"Child1", "Child2"})
public class Person {

    @JsonProperty("Child1")
    public String child1;

    @JsonProperty("Child2")
    public String child2;
}
Drejc
  • 14,196
  • 16
  • 71
  • 106
  • I don't have a java class representation. The input XML structure must be dynamic and can change. – Philipp Oct 22 '14 at 08:14
-1

You can keep order of incoming data when modify

private final Map<String, Object> nameValuePairs;

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

to

private final Map<String, Object> nameValuePairs;

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

Because instead of HashMap - LinkedHashMap have an predictable iteration order.

LinkedHashMap : Hash table and linked list implementation of the Map interface, with predictable iteration order.

So is the most effective way to resolve your problem.

And also you can fork to use a custom library from

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

Sergey Shustikov
  • 15,377
  • 12
  • 67
  • 119