org.json.simple JSONObject order in which items are inserted - java

I have an application which passes json data to a third-party application. The third party app requires that the fields in a JSONObject passed to it be in a certain order, or it returns an error "bad request".
My java code reads, in part:
import org.json.simple.JSONObject;
public void postThirdParty (String name, String info, long offset, JSONArray address)
{
JSONObject obj = new JSONObject ();
obj.put ("name", name);
obj.put ("info", info);
obj.put ("offset", offset);
obj.put ("address", address);
system.out.println (obj.toString ());
}
The output is:
{"name":"My Name","info":"My Info","address":[{"addressLine1":"My House No","addressLine2":"My Street","addressTown":"My Town","addressPostCode":"My Postcode"}],"offset":"My offset"}
Any idea why the fourth item to go into the JSONObject is being inserted ahead of the third?

JSONObject does not preserve the order in which the elements were inserted. The underlying implementation of most JSONObjects is a HashMap (which does not preserve the order).
From the docs:
A JSON object. Key value pairs are unordered. JSONObject supports
java.util.Map interface.
Not recommended, but if you are still looking for ordered keys in a HashMap, you can override the .put(...) and initialize the internal Map with a LinkedHashMap instead of a HashMap which will preserve the order.

Related

Inverted order of JSON elements in Java after XML conversion

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)?
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:
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>();
}
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>();
}
}
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.
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"}]}
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)
If you are hell bent on getting the output ordered the way you want it you could always try overriding the method
toString()
You can download the source code from http://www.json.org/java/ and modify JSONObject.java using TreeMap instead of HashMap.
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.
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;
}
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

Iterating the JSON object using Java not working exactly

I'm trying to get one JSON object and iterate it. In this case it should iterate only once because I'm having only one complete JSON object, if it has multiple, it should iterate multiple times. But rather it shows the system.out.println string twice. I don't understand whats wrong in this code?
String str = "{\"userId\":\"1234\",\"businessPrimaryInfo\":{\"53bd2a4\":{\"businessSpecificInfo\":{\"businessType\":\"Manufacturing\",\"tradingPlace\":\"Hyderabad\"}},\"53bd2a4e\":{\"businessSpecificInfo\":{\"businessType\":\"Milling\",\"tradingPlace\":\"Mumbai\"}}}}";
JSONObject jsonObj = new JSONObject(str);
Iterator<String> keys = jsonObj.keys();
// Iterate all the users.
while (keys.hasNext()) {
String key = keys.next();
try {
// If the user has Business primary info as JSON object.
if (jsonObj.has("businessPrimaryInfo")) {
JSONObject jsonBizPrimaryInfo = jsonObj
.getJSONObject("businessPrimaryInfo");
System.out.println("Object is:" + jsonBizPrimaryInfo);
}
} finally {
}
}
Please have a look and let me know where I'm doing wrong.
The two keys that are causing your loop to iterate twice are: userId and businessPrimaryInfo. But you ask the main JSON object if it has a businessPrimaryInfo key. Which it has for every iteration.
What you need to do is check that the current key is businessPrimaryInfo.
Or, you can just ask the primary object if it has a businessPrimaryInfo key, and if so, ask for that child object.

Json can work with sets

My question is how JSON works with serializing Sets because in my example , "propertyItems.getAddress().getCity().getProvinces().getCities()" is set of cities (Set) , i am iterating on it through for-each loop and putting it into json array. However it contains two cities i.e. MONTREAL and QuebecCity. But my url is only showing me "MONTREAL". Why its so? Json can't serialize sets provided to it?
JSONArray jarray = new JSONArray();
try {
JSONObject obj = new JSONObject();
for(Cities city:propertyItems.getAddress().getCity().getProvinces().getCities()){
obj.put("City Name",city.getCityname());
}
jarray.put(obj);
catch (JSONException e) {
logger.error(Constants.METHOD_INSIDE_MESSAGE +"getAuthors",e);
}
return jarray.toString();
}
** URL Output:** City Name :Montreal
The JSON specification states
The names within an object SHOULD be unique.
In the JSON parser/generator implementation you are using, ie. JSONObject, it seems the keys are unique. Every time you put an object with the same key, it overwrites the one added before it. Regardless of how many you put, the JSONObject will only contain one entry.

When JsonObject's keys are iterated they aren't in the same order as in the response from the server

I have a very large response from server of JSON string. I converted it to JSON object and then get the keys and iterate it.
The problem is that when I iterate it isnt in the same order as in response from server.
Next then I apply another method by adding all the keys in List<String> and the sort it and then get the iterator of that but still it isn't as I required (as in response).
Code example is here:
JSONObject jsonObject = new JSONObject(responseString);
Iterator<String> myIter = jsonObject.keys();
List<String> sortKey = new ArrayList<String>();
while(myIter.hasNext()){
sortKey.add(myIter.next());
}
Collections.sort(sortKey);
The order of the keys of a JSON object is not supposed to be meaningful. If you want a specific order, you should use an array, not an object.
Your Java code sorts the keys alphabetically. There is no way to get the initial ordering of the keys in the object.
Reference 1:
The order of the keys is undefined
Reference 2:
An object is an unordered set of name/value pairs
You can use Sorted map to put keys and values into. Something like this
public static List listFromJsonSorted(JSONObject json) {
if (json == null) return null;
SortedMap map = new TreeMap();
Iterator i = json.keys();
while (i.hasNext()) {
try {
String key = i.next().toString();
JSONObject j = json.getJSONObject(key);
map.put(key, j);
} catch (JSONException e) {
e.printStackTrace();
}
}
return new LinkedList(map.values());
}
I came across this similar problem while working on section in my android app which displays a list of 1024+ websites alphabetically. Since the json traversal was not in sorted order , I just inserted the json values during traversal into a table ( I m using list adapters in my app) and obtained sorted list of websites with cursor.
So if you are saving what you are fetching from server, you can just query your database to sort the values in the order your want.

put method in the json object adds value to the first of the jsonobject;

Consider following piece of code:
JSONObject json = new JSONObject();
json.put("one", 1);
json.put("two", 2);
json.put("three", 3);
If i print the jsonobject it prints like this
{"three":"1","two":"2","one":"1"}
But i want like this.
{"one":"1","two":"2","three":"3"}
Please help. Thanks in advance.
The documentation at http://www.json.org/javadoc/org/json/JSONObject.html says:
A JSONObject is an unordered collection of name/value pairs.
In other words, properties of an object are accessed by name, not by position and the default serialized form does not guarantee any specific order.
Strict positioning comes only with arrays:
JSONArray json = new JSONArray();
json.put("1");
json.put("2");
json.put("3");
json.toString(); // results in ["1", "2", "3"]
The easiest workaround to solve your problem is to use the sortedKeys() method and by iterating the JSONObject key by key, produce the JSON string manually in what ever order necessary. Implementing a custom Comparator might help also.

Categories