Preserve JSON key order during JSON to CSV conversion

I am using the JSON library provided here http://www.json.org/java/index.html to convert the json string I have in CSV. But I have a problem, the order of the keys is lost after the conversion.

This is the conversion code:

JSONObject jo = new JSONObject(someString); JSONArray ja = jo.getJSONArray("items"); String s = CDL.toString(ja); System.out.println(s); 

This is the contents of "someString":

 { "items": [ { "WR":"qwe", "QU":"asd", "QA":"end", "WO":"hasd", "NO":"qwer" }, ] } 

This is the result:

 WO,QU,WR,QA,NO hasd,asd,qwe,end,qwer 

While I expect this to keep the order of the keys:

 WR,QU,QA,WO,NO qwe,asd,end,hasd,qwer 

Is there any way to get this result using this library? If not, is there another library that will provide the ability to preserve the order of the keys as a result?

+61
java json csv
Dec 23 '10 at 3:40
source share
15 answers

solvable.

I used the JSON.simple library here https://code.google.com/p/json-simple/ to read the JSON string to keep the order of the keys and use the JavaCSV library from here http://sourceforge.net/projects/javacsv / to convert to CSV format.

+3
Dec 23 '10 at 6:29
source share

There are (hacker) ways to do this ... but you shouldn't.

In JSON, an object is defined this way:

An object is an unordered set of name / value pairs.

See http://json.org .

Most JSON implementations take no effort to preserve the order of the name / value pairs of an object, since it (by definition) is not significant.

If you want the order to be saved, you need to redefine the data structure; eg.

 { "items": [ [ {"WR":"qwe"}, {"QU":"asd"}, {"QA":"end"}, {"WO":"hasd"}, {"NO":"qwer"} ], ] } 

or more simply:

 { "items": [ {"WR":"qwe"}, {"QU":"asd"}, {"QA":"end"}, {"WO":"hasd"}, {"NO":"qwer"} ] } 

Followup

Thanks for the info, but I have no choice but to use JSON in my application, and my application needs to keep the key order regardless of the definition of the JSON object ... I am not allowed to change the format of the JSON file, as well ...

You need to have a tough conversation with the person who designed this file structure and will not let you change it. It is not right. You need to convince them.

If they really won't let you change it:

  • You must insist not to call it JSON ... 'Because it is not.
  • You must indicate that you will have to write / modify the code specifically to handle this non-JSON format ... if you cannot find some JSON implementation that saves the order. If they are a paid customer, make sure they pay for this extra work that you have to do.
  • You must indicate that if "not JSON" should be used by some other tool, this will be problematic. Indeed, this problem will occur again and again ...

Such a thing as very bad. On the one hand, your software will violate a well-established / long-standing specification designed to ensure interoperability. On the other hand, nit-wits that developed this chromatic (not JSON!) File format are likely to be removed from other people's systems, etc., because systems cannot handle their stupidity.

UPDATE

Also worth reading what says

+79
Dec 23 '10 at 4:28
source share

It’s very easy to keep order. I had the same problem with maintaining order from the database level to the user interface level.

Open the JSONObject.java file. It internally uses a HashMap that does not maintain order.

Change it to LinkedHashMap:

  //this.map = new HashMap(); this.map = new LinkedHashMap(); 

It worked for me. Let me know in the comments. I suggest that the JSON library itself has another JSONObject class that maintains order, such as JSONOrderdObject.java. I am very poor in choosing names.

+39
Nov 02 '11 at 14:05
source share

JSONObject.java takes any map you go through. It can be LinkedHashMap or TreeMap , and it will only have hashmap when the map is null.

Here is the constructor for the JSONObject.java class that will perform the map check.

  public JSONObject(Map paramMap) { this.map = (paramMap == null ? new HashMap() : paramMap); } 

So, before building the json object LinkedHashMap , and then passing it to a constructor like this,

 LinkedHashMap<String, String> jsonOrderedMap = new LinkedHashMap<String, String>(); jsonOrderedMap.put("1","red"); jsonOrderedMap.put("2","blue"); jsonOrderedMap.put("3","green"); JSONObject orderedJson = new JSONObject(jsonOrderedMap); JSONArray jsonArray = new JSONArray(Arrays.asList(orderedJson)); System.out.println("Ordered JSON Fianl CSV :: "+CDL.toString(jsonArray)); 

Therefore, there is no need to change the JSONObject.java class. Hope this helps someone.

+18
Jan 24 '13 at 8:13
source share

A more detailed, but widely applicable solution to this problem is to use a pair of data structures: a list that contains ordering, and a map that contains relationships.

Example:

 { "items": [ { "WR":"qwe", "QU":"asd", "QA":"end", "WO":"hasd", "NO":"qwer" }, ], "itemOrder": ["WR", "QU", "QA", "WO", "NO"] } 

You iterate over the itemOrder list and use them to find map values. The order is saved without curls.

I have used this method many times.

+10
Jun 11 '14 at 18:46
source share

Another hacking solution using reflection:

 JSONObject json = new JSONObject(); Field map = json.getClass().getDeclaredField("map"); map.setAccessible(true);//because the field is private final... map.set(json, new LinkedHashMap<>()); map.setAccessible(false);//return flag 
+8
Mar 04 '16 at 13:20
source share

Apache Wink has an OrderedJSONObject . It keeps order when parsing a string.

+5
Nov 21 '13 at 19:41
source share

I know this is a solution, and the question was asked a long time ago, but since I am dealing with a similar problem, I would like to give a completely different approach to this:

For arrays, he says: "An array is an ordered set of values." at http://www.json.org/ - but the objects ("The object is an unordered set of name / value pairs.") are not ordered.

I wonder why this object is in an array - this implies an order that does not exist.

 { "items": [ { "WR":"qwe", "QU":"asd", "QA":"end", "WO":"hasd", "NO":"qwer" }, ] } 

Thus, the solution would be to put the keys in a β€œreal” array and add data as objects to each key as follows:

 { "items": [ {"WR": {"data": "qwe"}}, {"QU": {"data": "asd"}}, {"QA": {"data": "end"}}, {"WO": {"data": "hasd"}}, {"NO": {"data": "qwer"}} ] } 

So, this is an approach that is trying to rethink the original modeling and its intent. But I didn’t test (and I’m interested) if all the tools involved kept the order of this original JSON array.

+3
Jan 19 '15 at 15:14
source share

Just stumbling upon the same problem, I believe that the final solution used by the author was to use a custom ContainerFactory:

 public static Values parseJSONToMap(String msgData) { JSONParser parser = new JSONParser(); ContainerFactory containerFactory = new ContainerFactory(){ @Override public Map createObjectContainer() { return new LinkedHashMap(); } @Override public List creatArrayContainer() { return null; } }; try { return (Map<String,Object>)parser.parse(msgData, containerFactory); } catch (ParseException e) { log.warn("Exception parsing JSON string {}", msgData, e); } return null; } 

see http://juliusdavies.ca/json-simple-1.1.1-javadocs/org/json/simple/parser/JSONParser.html#parse(java.io.Reader,org.json.simple.parser.ContainerFactory)

+2
Dec 04 '14 at 12:07
source share

The safest way is probably to override the key method that is used to generate the output:

 new JSONObject(){ @Override public Iterator keys(){ TreeSet<Object> sortedKeys = new TreeSet<Object>(); Iterator keys = super.keys(); while(keys.hasNext()){ sortedKeys.add(keys.next()); } return sortedKeys.iterator(); } }; 
0
Jun 04 '15 at 11:15
source share

patchFor (answer @gary):

 $ git diff JSONObject.java diff --git a/JSONObject.java b/JSONObject.java index e28c9cd..e12b7a0 100755 --- a/JSONObject.java +++ b/JSONObject.java @@ -32,7 +32,7 @@ import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.util.Collection; import java.util.Enumeration; -import java.util.HashMap; +import java.util.LinkedHashMap; import java.util.Iterator; import java.util.Locale; import java.util.Map; @@ -152,7 +152,9 @@ public class JSONObject { * Construct an empty JSONObject. */ public JSONObject() { - this.map = new HashMap<String, Object>(); +// this.map = new HashMap<String, Object>(); + // I want to keep order of the given data: + this.map = new LinkedHashMap<String, Object>(); } /** @@ -243,7 +245,7 @@ public class JSONObject { * @throws JSONException */ public JSONObject(Map<String, Object> map) { - this.map = new HashMap<String, Object>(); + this.map = new LinkedHashMap<String, Object>(); if (map != null) { Iterator<Entry<String, Object>> i = map.entrySet().iterator(); while (i.hasNext()) { 
0
Jul 10 '15 at
source share

You can use the following code for custom ORDERED serialization and JSON Array deserialization (in this example, it is assumed that you order strings, but can be applied to all types):

Serialization

 JSONArray params = new JSONArray(); int paramIndex = 0; for (String currParam : mParams) { JSONObject paramObject = new JSONObject(); paramObject.put("index", paramIndex); paramObject.put("value", currParam); params.put(paramObject); ++paramIndex; } json.put("orderedArray", params); 

Deserialization

 JSONArray paramsJsonArray = json.optJSONArray("orderedArray"); if (null != paramsJsonArray) { ArrayList<String> paramsArr = new ArrayList<>(); for (int i = 0; i < paramsJsonArray.length(); i++) { JSONObject param = paramsJsonArray.optJSONObject(i); if (null != param) { int paramIndex = param.optInt("index", -1); String paramValue = param.optString("value", null); if (paramIndex > -1 && null != paramValue) { paramsArr.add(paramIndex, paramValue); } } } } 
0
Jan 19 '16 at 17:11
source share

Your example:

 { "items": [ { "WR":"qwe", "QU":"asd", "QA":"end", "WO":"hasd", "NO":"qwer" }, ... ] } 

add itemorder element

 { "items": [ { "WR":"qwe", "QU":"asd", "QA":"end", "WO":"hasd", "NO":"qwer" }, ... ], "itemorder":["WR","QU","QA","WO","NO"] } 

This code generates the desired result without a column header line:

 JSONObject output = new JSONObject(json); JSONArray docs = output.getJSONArray("data"); JSONArray names = output.getJSONArray("itemOrder"); String csv = CDL.toString(names,docs); 
0
Jun 10 '16 at 19:32
source share

In the real world, an application will almost always have a java bean or domain that needs to be serialized / de-serialized to / from JSON. It has already been mentioned that the specification of a JSON object does not guarantee order, and any manipulation of this behavior does not justify this requirement. I had the same scenario in my application where I needed to save the order for read-only purposes. I used the standard Jackson method to serialize java bean for JSON:

 Object object = getObject(); //the source java bean that needs conversion String jsonString = new com.fasterxml.jackson.databind.ObjectMapper().writeValueAsString(object); 

To make json with an ordered set of elements, I just use the JSON properties annotation in the Java bean that I used to convert. Example below:

 @JsonInclude(JsonInclude.Include.NON_NULL) @JsonPropertyOrder({"name","phone","city","id"}) public class SampleBean implements Serializable { private int id; private String name: private String city; private String phone; //...standard getters and setters } 

the getObject () method used above:

 public SampleBean getObject(){ SampleBean bean = new SampleBean(); bean.setId("100"); bean.setName("SomeName"); bean.setCity("SomeCity"); bean.setPhone("1234567890"); return bean; } 

The result is displayed according to the Json property order annotation:

 { name: "SomeName", phone: "1234567890", city: "SomeCity", id: 100 } 
0
May 9 '17 at
source share

The wink solution has been tested and it works fine:

 @Test public void testJSONObject() { JSONObject jsonObject = new JSONObject(); jsonObject.put("bbb", "xxx"); jsonObject.put("ccc", "xxx"); jsonObject.put("aaa", "xxx"); jsonObject.put("xxx", "xxx"); System.out.println(jsonObject.toString()); assertTrue(jsonObject.toString().startsWith("{\"xxx\":")); } @Test public void testWinkJSONObject() throws JSONException { org.apache.wink.json4j.JSONObject jsonObject = new OrderedJSONObject(); jsonObject.put("bbb", "xxx"); jsonObject.put("ccc", "xxx"); jsonObject.put("aaa", "xxx"); jsonObject.put("xxx", "xxx"); assertEquals("{\"bbb\":\"xxx\",\"ccc\":\"xxx\",\"aaa\":\"xxx\",\"xxx\":\"xxx\"}", jsonObject.toString()); } 
-one
Aug 05 '15 at 10:11
source share



All Articles