I am using a JSONObject in order to remove a certin attribute I don't need in a JSON String:
JSONObject jsonObject = new JSONObject(jsonString);
jsonObject.remove("owner");
jsonString = jsonObject.toString();
It works ok however the problem is that the JSONObject is "an unordered collection of name/value pairs" and I want to maintain the original order the String had before it went through the JSONObject manipulation.
Any idea how to do this?
try this
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();
You can't.
That is why we call it an unordered collection of name/value pairs.
Why you would need to do this, I'm not sure. But if you want ordering, you'll have to use a json array.
I have faced the same problem recently and just transitioned all our tests (which expect JSON attributes to be in the same order) to another JSON library:
<dependency>
<groupId>org.codehaus.jettison</groupId>
<artifactId>jettison</artifactId>
<version>1.3.5</version>
</dependency>
Internally it uses a LinkedHashMap, which maintains the order of attributes. This library is functionally equivalent to the json.org library, so I don't see any reason why not use it instead, at least for tests.
You can go for the JsonObject provided by the com.google.gson it is nearly the same with the JSONObject by org.json but some different functions.
For converting String to Json object and also maintains the order you can use:
Gson gson = new Gson();
JsonObject jsonObject = gson.fromJson(<Json String>, JsonObject.class);
For eg:-
String jsonString = "your json String";
JsonObject jsonObject = gson.fromJson(jsonString, JsonObject.class);
It just maintains the order of the JsonObject from the String.
If you can edit the server repose then change it to array of JSON objects.
JSON:
[
{PropertyName:"Date of Issue:",PropertyValue:"3/21/2011"},
PropertyName:"Report No:",PropertyValue:"2131196186"},{PropertyName:"Weight:",PropertyValue:"1.00"},
{PropertyName:"Report Type:",PropertyValue:"DG"}
]
And I handled it with JSONArray in client side (Android):
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.
}
You can use Jsckson library in case to maintain the order of Json keys.
It internally uses 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;
The code to remove a field, the removed JsonToken could itself be read if required.
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 implementation uses a LinkedHashMap, which maintains the insertion order:
public ObjectNode(JsonNodeFactory nc) {
super(nc);
_children = new LinkedHashMap<String, JsonNode>();
}
Go on JSONObject class
Change from HashMap() to LinkedHashMap()
/**
* Construct an empty JSONObject.
*/
public JSONObject() {
this.map = new LinkedHashMap();
}
The LinkedHashMap class extends the Hashmap class. This class uses a doubly linked list containing all the entries of the hashed table, in the order in which the keys were inserted in the table: this allows the keys to be "ordered".
This is not easy, the main idea is to use LinkedHashMap, either pass in to the constructor (JSONObject(Map map)), or modify bytecode to handle the String parameter (JSONObject(String source)), which is the main use case. I got a solution in 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);
}
}
need to include jar file from using mvn
<dependency>
<groupId>javassist</groupId>
<artifactId>javassist</artifactId>
<version>3.12.1.GA</version>
</dependency>
From Android 20, JSONObject preserves the order as it uses LinkedHashMap to store namevaluepairs. Android 19 and below uses HashMap to store namevaluepairs. So, Android 19 and below doesn't preserve the order. If you are using 20 or above, don't worry, JSONObject will preserve the order. Or else, use JSONArray instead.
In JDK 8 and above, We can do it by using nashorn engine, supported in JDK 8.
Java 8 support to use js engine to evaluate:
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 );"
);
I was able to do this with help of classpath overriding.
created package package org.json.simple which is same as in jar and class named as JSONObject.
Took existing code from jar and updated the class by extending LinkedHashmap instead of Hashmap
by doing these 2 steps it will maintain the order, because preference of picking `JSONObject will be higher to pick from the new package created in step 1 than the jar.
I accomplished it by doing a:
JSONObject(json).put(key, ObjectMapper().writeValueAsString(ObjectMapper().readValue(string, whatever::class)))
So essentially I deserialize a string to an ordered class, then I serialize it again. But then I also had to format that string afterwards to remove escapes.
.replace("\\\"", "\"").replace("\"{", "{").replace("}\"", "}")
You may also have to replace null items as well if you don't want nulls.
Related
I need to find a way how to merge two (or more) JSON objects into a single JSON object without JSON arrays.
There's code example:
public static void mergeJSONs() {
JSONObject jsonObject1 = new JSONObject("{\"1level1\":{\"1level2\":{\"1label1\":\"1value1\"}}}");
JSONObject jsonObject2 = new JSONObject("{\"1level1\":{\"1level2\":{\"1label2\":\"1value2\"}}}");
JSONObject jsonObject3 = new JSONObject("{\"2level1\":{\"2level2\":{\"2level3\":{\"2label1\":\"2value1\"}}}}");
JSONObject jsonObject4 = new JSONObject("{\"2level1\":{\"2level2\":{\"2label2\":\"2value2\"}}}");
JSONObject combined = new JSONObject();
combined.put("strings", jsonObject1);
combined.put("strings", jsonObject2);
combined.put("strings", jsonObject3);
combined.put("strings", jsonObject4);
System.out.println(combined.toString());
}
Output for this code:
{"strings":{"2level1":{"2level2":{"2label2":"2value2"}}}}
Expected output:
{
"strings":{
"1level1":{
"1level2":{
"1label1":"1value1",
"1label2":"1value2"
}
},
"2level1":{
"2level2":{
"2level3":{
"2label1":"2value1"
},
"2label2":"2value2"
}
}
}
}
or in one line:
{"strings":{"1level1":{"1level2":{"1label1":"1value1","1label2":"1value2"}},"2level1":{"2level2":{"2level3":{"2label1":"2value1"},"2label2":"2value2"}}}}
The thing is that I will never know the names of the JSON objects and how deep will this JSON be in the end as this will be used to convert files, so I am not able to create POJO for this.
Currently I'm using org.json library for this but if there is an easy way how to do that with other libraries - it works for me as well.
Library:
<dependency>
<groupId>org.json</groupId>
<artifactId>json</artifactId>
<version>20180813</version>
</dependency>
UPDATE #1:
Those objects are provided just for example, in reality JSONs will be larger and deeper. Problem is that I will never know how deep and which keys matches in JSON. So here recursive method is needed which would go through both object until there is any key that matches and one of object doesn't contain that key then all the nested objects should be merged to that one.
UPDATE #2:
Updated sample code and expected output to make image more clear
I prefer and recommend use Gson from Google:
The last release is 2.8.6 from Oct of 2019:
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.8.6</version>
</dependency>
Always check last version on Maven Central Repository:
https://mvnrepository.com/artifact/com.google.code.gson/gson
Example:
public static void mergeJSONs() {
String JSON1 = "{\"1level1\":{\"1level2\":{\"1label1\":\"1value1\"}}}";
String JSON2 = "{\"1level1\":{\"1level2\":{\"1label2\":\"1value2\"}}}";
String JSON3 = "{\"2level1\":{\"2level2\":{\"2level3\":{\"2label1\":\"2value1\"}}}}";
String JSON4 = "{\"2level1\":{\"2level2\":{\"2label2\":\"2value2\"}}}";
String finalJson = organizeJson(JSON1, JSON2, JSON3, JSON4);
System.out.println(finalJson);
}
The method can receive a list of json payloads, add a root element and merge:
public String organizeJson(String... jsonList) throws Exception {
JsonObject jsonObj = null;
Gson gson = new GsonBuilder().setPrettyPrinting().create();
for (String json : jsonList) {
if (jsonObj != null) {
jsonObj = jsonMerge(jsonObj, gson.fromJson(json, JsonObject.class));
} else {
jsonObj = gson.fromJson(json, JsonObject.class);
}
}
JsonObject jsonStringsRoot = new JsonObject();
/* Add "strings" as root element */
jsonStringsRoot.add("strings", jsonObj);
return gson.toJson(jsonStringsRoot);
}
Method using recursive call to find the last level on nested objects (deep merge):
public static JsonObject jsonMerge(JsonObject jsonA, JsonObject jsonB) throws Exception {
for (Map.Entry<String, JsonElement> sourceEntry : jsonA.entrySet()) {
String key = sourceEntry.getKey();
JsonElement value = sourceEntry.getValue();
if (!jsonB.has(key)) {
if (!value.isJsonNull()) {
jsonB.add(key, value);
}
} else {
if (!value.isJsonNull()) {
if (value.isJsonObject()) {
jsonMerge(value.getAsJsonObject(), jsonB.get(key).getAsJsonObject());
} else {
jsonB.add(key, value);
}
} else {
jsonB.remove(key);
}
}
}
return jsonB;
}
Reference:
https://stackoverflow.com/a/38757661/5626568
I've a problem trying to make my page printing out the JSONObject in the order i want. In my code, I entered this:
JSONObject myObject = new JSONObject();
myObject.put("userid", "User 1");
myObject.put("amount", "24.23");
myObject.put("success", "NO");
However, when I see the display on my page, it gives:
JSON formatted string: [{"success":"NO", "userid":"User 1", "bid":24.23}]
I need it in the order of userid, amount, then success. Already tried re-ordering in the code, but to no avail. I've also tried .append....need some help here thanks!!
You cannot and should not rely on the ordering of elements within a JSON object.
From the JSON specification at https://www.json.org/
An object is an unordered set of
name/value pairs
As a consequence,
JSON libraries are free to rearrange the order of the elements as they see fit.
This is not a bug.
I agree with the other answers. You cannot rely on the ordering of JSON elements.
However if we need to have an ordered JSON, one solution might be to prepare a LinkedHashMap object with elements and convert it to JSONObject.
#Test
def void testOrdered() {
Map obj = new LinkedHashMap()
obj.put("a", "foo1")
obj.put("b", new Integer(100))
obj.put("c", new Double(1000.21))
obj.put("d", new Boolean(true))
obj.put("e", "foo2")
obj.put("f", "foo3")
obj.put("g", "foo4")
obj.put("h", "foo5")
obj.put("x", null)
JSONObject json = (JSONObject) obj
logger.info("Ordered Json : %s", json.toString())
String expectedJsonString = """{"a":"foo1","b":100,"c":1000.21,"d":true,"e":"foo2","f":"foo3","g":"foo4","h":"foo5"}"""
assertEquals(expectedJsonString, json.toString())
JSONAssert.assertEquals(JSONSerializer.toJSON(expectedJsonString), json)
}
Normally the order is not preserved as below.
#Test
def void testUnordered() {
Map obj = new HashMap()
obj.put("a", "foo1")
obj.put("b", new Integer(100))
obj.put("c", new Double(1000.21))
obj.put("d", new Boolean(true))
obj.put("e", "foo2")
obj.put("f", "foo3")
obj.put("g", "foo4")
obj.put("h", "foo5")
obj.put("x", null)
JSONObject json = (JSONObject) obj
logger.info("Unordered Json : %s", json.toString(3, 3))
String unexpectedJsonString = """{"a":"foo1","b":100,"c":1000.21,"d":true,"e":"foo2","f":"foo3","g":"foo4","h":"foo5"}"""
// string representation of json objects are different
assertFalse(unexpectedJsonString.equals(json.toString()))
// json objects are equal
JSONAssert.assertEquals(JSONSerializer.toJSON(unexpectedJsonString), json)
}
You may check my post too: http://www.flyingtomoon.com/2011/04/preserving-order-in-json.html
u can retain the order, if u use JsonObject that belongs to com.google.gson :D
JsonObject responseObj = new JsonObject();
responseObj.addProperty("userid", "User 1");
responseObj.addProperty("amount", "24.23");
responseObj.addProperty("success", "NO");
Usage of this JsonObject doesn't even bother using Map<>
CHEERS!!!
Real answer can be found in specification, json is unordered.
However as a human reader I ordered my elements in order of importance. Not only is it a more logic way, it happened to be easier to read. Maybe the author of the specification never had to read JSON, I do.. So, Here comes a fix:
/**
* I got really tired of JSON rearranging added properties.
* Specification states:
* "An object is an unordered set of name/value pairs"
* StackOverflow states:
* As a consequence, JSON libraries are free to rearrange the order of the elements as they see fit.
* I state:
* My implementation will freely arrange added properties, IN SEQUENCE ORDER!
* Why did I do it? Cause of readability of created JSON document!
*/
private static class OrderedJSONObjectFactory {
private static Logger log = Logger.getLogger(OrderedJSONObjectFactory.class.getName());
private static boolean setupDone = false;
private static Field JSONObjectMapField = null;
private static void setupFieldAccessor() {
if( !setupDone ) {
setupDone = true;
try {
JSONObjectMapField = JSONObject.class.getDeclaredField("map");
JSONObjectMapField.setAccessible(true);
} catch (NoSuchFieldException ignored) {
log.warning("JSONObject implementation has changed, returning unmodified instance");
}
}
}
private static JSONObject create() {
setupFieldAccessor();
JSONObject result = new JSONObject();
try {
if (JSONObjectMapField != null) {
JSONObjectMapField.set(result, new LinkedHashMap<>());
}
}catch (IllegalAccessException ignored) {}
return result;
}
}
from lemiorhan example
i can solve with just change some line of lemiorhan's code
use:
JSONObject json = new JSONObject(obj);
instead of this:
JSONObject json = (JSONObject) obj
so in my test code is :
Map item_sub2 = new LinkedHashMap();
item_sub2.put("name", "flare");
item_sub2.put("val1", "val1");
item_sub2.put("val2", "val2");
item_sub2.put("size",102);
JSONArray itemarray2 = new JSONArray();
itemarray2.add(item_sub2);
itemarray2.add(item_sub2);//just for test
itemarray2.add(item_sub2);//just for test
Map item_sub1 = new LinkedHashMap();
item_sub1.put("name", "flare");
item_sub1.put("val1", "val1");
item_sub1.put("val2", "val2");
item_sub1.put("children",itemarray2);
JSONArray itemarray = new JSONArray();
itemarray.add(item_sub1);
itemarray.add(item_sub1);//just for test
itemarray.add(item_sub1);//just for test
Map item_root = new LinkedHashMap();
item_root.put("name", "flare");
item_root.put("children",itemarray);
JSONObject json = new JSONObject(item_root);
System.out.println(json.toJSONString());
JavaScript objects, and JSON, have no way to set the order for the keys. You might get it right in Java (I don't know how Java objects work, really) but if it's going to a web client or another consumer of the JSON, there is no guarantee as to the order of keys.
Download "json simple 1.1 jar" from this https://code.google.com/p/json-simple/downloads/detail?name=json_simple-1.1.jar&can=2&q=
And add the jar file to your lib folder
using JSONValue you can convert LinkedHashMap to json string
For those who're using maven, please try com.github.tsohr/json
<!-- https://mvnrepository.com/artifact/com.github.tsohr/json -->
<dependency>
<groupId>com.github.tsohr</groupId>
<artifactId>json</artifactId>
<version>0.0.1</version>
</dependency>
It's forked from JSON-java but switch its map implementation with LinkedHashMap which #lemiorhan noted above.
As all are telling you, JSON does not maintain "sequence" but array does, maybe this could convince you:
Ordered JSONObject
For Java code, Create a POJO class for your object instead of a JSONObject.
and use JSONEncapsulator for your POJO class.
that way order of elements depends on the order of getter setters in your POJO class.
for eg. POJO class will be like
Class myObj{
String userID;
String amount;
String success;
// getter setters in any order that you want
and where you need to send your json object in response
JSONContentEncapsulator<myObj> JSONObject = new JSONEncapsulator<myObj>("myObject");
JSONObject.setObject(myObj);
return Response.status(Status.OK).entity(JSONObject).build();
The response of this line will be
{myObject : {//attributes order same as getter setter order.}}
The main intention here is to send an ordered JSON object as response. We don't need javax.json.JsonObject to achieve that. We could create the ordered json as a string.
First create a LinkedHashMap with all key value pairs in required order. Then generate the json in string as shown below.
Its much easier with Java 8.
public Response getJSONResponse() {
Map<String, String> linkedHashMap = new LinkedHashMap<>();
linkedHashMap.put("A", "1");
linkedHashMap.put("B", "2");
linkedHashMap.put("C", "3");
String jsonStr = linkedHashMap.entrySet().stream()
.map(x -> "\"" + x.getKey() + "\":\"" + x.getValue() + "\"")
.collect(Collectors.joining(",", "{", "}"));
return Response.ok(jsonStr).build();
}
The response return by this function would be following:
{"A":"1","B":"2","C":"3"}
Underscore-java uses linkedhashmap to store key/value for json. I am the maintainer of the project.
Map<String, Object> myObject = new LinkedHashMap<>();
myObject.put("userid", "User 1");
myObject.put("amount", "24.23");
myObject.put("success", "NO");
System.out.println(U.toJson(myObject));
I found a "neat" reflection tweak on "the interwebs" that I like to share.
(origin: https://towardsdatascience.com/create-an-ordered-jsonobject-in-java-fb9629247d76)
It is about to change underlying collection in org.json.JSONObject to an un-ordering one (LinkedHashMap) by reflection API.
I tested succesfully:
import java.lang.reflect.Field;
import java.util.LinkedHashMap;
import org.json.JSONObject;
private static void makeJSONObjLinear(JSONObject jsonObject) {
try {
Field changeMap = jsonObject.getClass().getDeclaredField("map");
changeMap.setAccessible(true);
changeMap.set(jsonObject, new LinkedHashMap<>());
changeMap.setAccessible(false);
} catch (IllegalAccessException | NoSuchFieldException e) {
e.printStackTrace();
}
}
[...]
JSONObject requestBody = new JSONObject();
makeJSONObjLinear(requestBody);
requestBody.put("username", login);
requestBody.put("password", password);
[...]
// returned '{"username": "billy_778", "password": "********"}' == unordered
// instead of '{"password": "********", "username": "billy_778"}' == ordered (by key)
Just add the order with this tag
#JsonPropertyOrder({ "property1", "property2"})
Not sure if I am late to the party but I found this nice example that overrides the JSONObject constructor and makes sure that the JSON data are output in the same way as they are added. Behind the scenes JSONObject uses the MAP and MAP does not guarantee the order hence we need to override it to make sure we are receiving our JSON as per our order.
If you add this to your JSONObject then the resulting JSON would be in the same order as you have created it.
import java.io.IOException;
import java.lang.reflect.Field;
import java.util.LinkedHashMap;
import org.json.JSONObject;
import lombok.extern.java.Log;
#Log
public class JSONOrder {
public static void main(String[] args) throws IOException {
JSONObject jsontest = new JSONObject();
try {
Field changeMap = jsonEvent.getClass().getDeclaredField("map");
changeMap.setAccessible(true);
changeMap.set(jsonEvent, new LinkedHashMap<>());
changeMap.setAccessible(false);
} catch (IllegalAccessException | NoSuchFieldException e) {
log.info(e.getMessage());
}
jsontest.put("one", "I should be first");
jsonEvent.put("two", "I should be second");
jsonEvent.put("third", "I should be third");
System.out.println(jsonEvent);
}
}
Just use LinkedHashMap to keep de order and transform it to json with jackson
import com.fasterxml.jackson.databind.ObjectMapper;
import java.util.LinkedHashMap;
LinkedHashMap<String, Object> obj = new LinkedHashMap<String, Object>();
stats.put("aaa", "aaa");
stats.put("bbb", "bbb");
stats.put("ccc", "ccc");
ObjectMapper mapper = new ObjectMapper();
String json = mapper.writerWithDefaultPrettyPrinter().writeValueAsString(obj);
System.out.println(json);
maven dependency
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.9.10.7</version>
</dependency>
I just want the order for android unit tests that are somehow randomly changing overtime with this cool org.json.JSONObject, even thou it looks like it uses linked map but probably depends on api you compile it with or something, so it has different impl. with different android api probably.
I would suggest something like this:
object Json {
#SuppressLint("DiscouragedPrivateApi")
fun Object() = org.json.JSONObject().apply {
runCatching {
val nameValuePairs: Field = javaClass.getDeclaredField("nameValuePairs")
nameValuePairs.isAccessible = true
nameValuePairs.set(this, LinkedHashMap<String, Any?>())
}.onFailure { it.printStackTrace() }
}
}
Usage:
val jsonObject = Json.Object()
...
This is just some possibility I use it little differently so I modified it to post here. Sure gson or other lib is another option.
Suggestions that specification is bla bla are so shortsighted here, why you guys even post it, who cares about 15 years old json spec, everyone wants it ordered anyway.
I've a problem trying to make my page printing out the JSONObject in the order i want. In my code, I entered this:
JSONObject myObject = new JSONObject();
myObject.put("userid", "User 1");
myObject.put("amount", "24.23");
myObject.put("success", "NO");
However, when I see the display on my page, it gives:
JSON formatted string: [{"success":"NO", "userid":"User 1", "bid":24.23}]
I need it in the order of userid, amount, then success. Already tried re-ordering in the code, but to no avail. I've also tried .append....need some help here thanks!!
You cannot and should not rely on the ordering of elements within a JSON object.
From the JSON specification at https://www.json.org/
An object is an unordered set of
name/value pairs
As a consequence,
JSON libraries are free to rearrange the order of the elements as they see fit.
This is not a bug.
I agree with the other answers. You cannot rely on the ordering of JSON elements.
However if we need to have an ordered JSON, one solution might be to prepare a LinkedHashMap object with elements and convert it to JSONObject.
#Test
def void testOrdered() {
Map obj = new LinkedHashMap()
obj.put("a", "foo1")
obj.put("b", new Integer(100))
obj.put("c", new Double(1000.21))
obj.put("d", new Boolean(true))
obj.put("e", "foo2")
obj.put("f", "foo3")
obj.put("g", "foo4")
obj.put("h", "foo5")
obj.put("x", null)
JSONObject json = (JSONObject) obj
logger.info("Ordered Json : %s", json.toString())
String expectedJsonString = """{"a":"foo1","b":100,"c":1000.21,"d":true,"e":"foo2","f":"foo3","g":"foo4","h":"foo5"}"""
assertEquals(expectedJsonString, json.toString())
JSONAssert.assertEquals(JSONSerializer.toJSON(expectedJsonString), json)
}
Normally the order is not preserved as below.
#Test
def void testUnordered() {
Map obj = new HashMap()
obj.put("a", "foo1")
obj.put("b", new Integer(100))
obj.put("c", new Double(1000.21))
obj.put("d", new Boolean(true))
obj.put("e", "foo2")
obj.put("f", "foo3")
obj.put("g", "foo4")
obj.put("h", "foo5")
obj.put("x", null)
JSONObject json = (JSONObject) obj
logger.info("Unordered Json : %s", json.toString(3, 3))
String unexpectedJsonString = """{"a":"foo1","b":100,"c":1000.21,"d":true,"e":"foo2","f":"foo3","g":"foo4","h":"foo5"}"""
// string representation of json objects are different
assertFalse(unexpectedJsonString.equals(json.toString()))
// json objects are equal
JSONAssert.assertEquals(JSONSerializer.toJSON(unexpectedJsonString), json)
}
You may check my post too: http://www.flyingtomoon.com/2011/04/preserving-order-in-json.html
u can retain the order, if u use JsonObject that belongs to com.google.gson :D
JsonObject responseObj = new JsonObject();
responseObj.addProperty("userid", "User 1");
responseObj.addProperty("amount", "24.23");
responseObj.addProperty("success", "NO");
Usage of this JsonObject doesn't even bother using Map<>
CHEERS!!!
Real answer can be found in specification, json is unordered.
However as a human reader I ordered my elements in order of importance. Not only is it a more logic way, it happened to be easier to read. Maybe the author of the specification never had to read JSON, I do.. So, Here comes a fix:
/**
* I got really tired of JSON rearranging added properties.
* Specification states:
* "An object is an unordered set of name/value pairs"
* StackOverflow states:
* As a consequence, JSON libraries are free to rearrange the order of the elements as they see fit.
* I state:
* My implementation will freely arrange added properties, IN SEQUENCE ORDER!
* Why did I do it? Cause of readability of created JSON document!
*/
private static class OrderedJSONObjectFactory {
private static Logger log = Logger.getLogger(OrderedJSONObjectFactory.class.getName());
private static boolean setupDone = false;
private static Field JSONObjectMapField = null;
private static void setupFieldAccessor() {
if( !setupDone ) {
setupDone = true;
try {
JSONObjectMapField = JSONObject.class.getDeclaredField("map");
JSONObjectMapField.setAccessible(true);
} catch (NoSuchFieldException ignored) {
log.warning("JSONObject implementation has changed, returning unmodified instance");
}
}
}
private static JSONObject create() {
setupFieldAccessor();
JSONObject result = new JSONObject();
try {
if (JSONObjectMapField != null) {
JSONObjectMapField.set(result, new LinkedHashMap<>());
}
}catch (IllegalAccessException ignored) {}
return result;
}
}
from lemiorhan example
i can solve with just change some line of lemiorhan's code
use:
JSONObject json = new JSONObject(obj);
instead of this:
JSONObject json = (JSONObject) obj
so in my test code is :
Map item_sub2 = new LinkedHashMap();
item_sub2.put("name", "flare");
item_sub2.put("val1", "val1");
item_sub2.put("val2", "val2");
item_sub2.put("size",102);
JSONArray itemarray2 = new JSONArray();
itemarray2.add(item_sub2);
itemarray2.add(item_sub2);//just for test
itemarray2.add(item_sub2);//just for test
Map item_sub1 = new LinkedHashMap();
item_sub1.put("name", "flare");
item_sub1.put("val1", "val1");
item_sub1.put("val2", "val2");
item_sub1.put("children",itemarray2);
JSONArray itemarray = new JSONArray();
itemarray.add(item_sub1);
itemarray.add(item_sub1);//just for test
itemarray.add(item_sub1);//just for test
Map item_root = new LinkedHashMap();
item_root.put("name", "flare");
item_root.put("children",itemarray);
JSONObject json = new JSONObject(item_root);
System.out.println(json.toJSONString());
JavaScript objects, and JSON, have no way to set the order for the keys. You might get it right in Java (I don't know how Java objects work, really) but if it's going to a web client or another consumer of the JSON, there is no guarantee as to the order of keys.
Download "json simple 1.1 jar" from this https://code.google.com/p/json-simple/downloads/detail?name=json_simple-1.1.jar&can=2&q=
And add the jar file to your lib folder
using JSONValue you can convert LinkedHashMap to json string
For those who're using maven, please try com.github.tsohr/json
<!-- https://mvnrepository.com/artifact/com.github.tsohr/json -->
<dependency>
<groupId>com.github.tsohr</groupId>
<artifactId>json</artifactId>
<version>0.0.1</version>
</dependency>
It's forked from JSON-java but switch its map implementation with LinkedHashMap which #lemiorhan noted above.
As all are telling you, JSON does not maintain "sequence" but array does, maybe this could convince you:
Ordered JSONObject
For Java code, Create a POJO class for your object instead of a JSONObject.
and use JSONEncapsulator for your POJO class.
that way order of elements depends on the order of getter setters in your POJO class.
for eg. POJO class will be like
Class myObj{
String userID;
String amount;
String success;
// getter setters in any order that you want
and where you need to send your json object in response
JSONContentEncapsulator<myObj> JSONObject = new JSONEncapsulator<myObj>("myObject");
JSONObject.setObject(myObj);
return Response.status(Status.OK).entity(JSONObject).build();
The response of this line will be
{myObject : {//attributes order same as getter setter order.}}
The main intention here is to send an ordered JSON object as response. We don't need javax.json.JsonObject to achieve that. We could create the ordered json as a string.
First create a LinkedHashMap with all key value pairs in required order. Then generate the json in string as shown below.
Its much easier with Java 8.
public Response getJSONResponse() {
Map<String, String> linkedHashMap = new LinkedHashMap<>();
linkedHashMap.put("A", "1");
linkedHashMap.put("B", "2");
linkedHashMap.put("C", "3");
String jsonStr = linkedHashMap.entrySet().stream()
.map(x -> "\"" + x.getKey() + "\":\"" + x.getValue() + "\"")
.collect(Collectors.joining(",", "{", "}"));
return Response.ok(jsonStr).build();
}
The response return by this function would be following:
{"A":"1","B":"2","C":"3"}
Underscore-java uses linkedhashmap to store key/value for json. I am the maintainer of the project.
Map<String, Object> myObject = new LinkedHashMap<>();
myObject.put("userid", "User 1");
myObject.put("amount", "24.23");
myObject.put("success", "NO");
System.out.println(U.toJson(myObject));
I found a "neat" reflection tweak on "the interwebs" that I like to share.
(origin: https://towardsdatascience.com/create-an-ordered-jsonobject-in-java-fb9629247d76)
It is about to change underlying collection in org.json.JSONObject to an un-ordering one (LinkedHashMap) by reflection API.
I tested succesfully:
import java.lang.reflect.Field;
import java.util.LinkedHashMap;
import org.json.JSONObject;
private static void makeJSONObjLinear(JSONObject jsonObject) {
try {
Field changeMap = jsonObject.getClass().getDeclaredField("map");
changeMap.setAccessible(true);
changeMap.set(jsonObject, new LinkedHashMap<>());
changeMap.setAccessible(false);
} catch (IllegalAccessException | NoSuchFieldException e) {
e.printStackTrace();
}
}
[...]
JSONObject requestBody = new JSONObject();
makeJSONObjLinear(requestBody);
requestBody.put("username", login);
requestBody.put("password", password);
[...]
// returned '{"username": "billy_778", "password": "********"}' == unordered
// instead of '{"password": "********", "username": "billy_778"}' == ordered (by key)
Just add the order with this tag
#JsonPropertyOrder({ "property1", "property2"})
Not sure if I am late to the party but I found this nice example that overrides the JSONObject constructor and makes sure that the JSON data are output in the same way as they are added. Behind the scenes JSONObject uses the MAP and MAP does not guarantee the order hence we need to override it to make sure we are receiving our JSON as per our order.
If you add this to your JSONObject then the resulting JSON would be in the same order as you have created it.
import java.io.IOException;
import java.lang.reflect.Field;
import java.util.LinkedHashMap;
import org.json.JSONObject;
import lombok.extern.java.Log;
#Log
public class JSONOrder {
public static void main(String[] args) throws IOException {
JSONObject jsontest = new JSONObject();
try {
Field changeMap = jsonEvent.getClass().getDeclaredField("map");
changeMap.setAccessible(true);
changeMap.set(jsonEvent, new LinkedHashMap<>());
changeMap.setAccessible(false);
} catch (IllegalAccessException | NoSuchFieldException e) {
log.info(e.getMessage());
}
jsontest.put("one", "I should be first");
jsonEvent.put("two", "I should be second");
jsonEvent.put("third", "I should be third");
System.out.println(jsonEvent);
}
}
Just use LinkedHashMap to keep de order and transform it to json with jackson
import com.fasterxml.jackson.databind.ObjectMapper;
import java.util.LinkedHashMap;
LinkedHashMap<String, Object> obj = new LinkedHashMap<String, Object>();
stats.put("aaa", "aaa");
stats.put("bbb", "bbb");
stats.put("ccc", "ccc");
ObjectMapper mapper = new ObjectMapper();
String json = mapper.writerWithDefaultPrettyPrinter().writeValueAsString(obj);
System.out.println(json);
maven dependency
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.9.10.7</version>
</dependency>
I just want the order for android unit tests that are somehow randomly changing overtime with this cool org.json.JSONObject, even thou it looks like it uses linked map but probably depends on api you compile it with or something, so it has different impl. with different android api probably.
I would suggest something like this:
object Json {
#SuppressLint("DiscouragedPrivateApi")
fun Object() = org.json.JSONObject().apply {
runCatching {
val nameValuePairs: Field = javaClass.getDeclaredField("nameValuePairs")
nameValuePairs.isAccessible = true
nameValuePairs.set(this, LinkedHashMap<String, Any?>())
}.onFailure { it.printStackTrace() }
}
}
Usage:
val jsonObject = Json.Object()
...
This is just some possibility I use it little differently so I modified it to post here. Sure gson or other lib is another option.
Suggestions that specification is bla bla are so shortsighted here, why you guys even post it, who cares about 15 years old json spec, everyone wants it ordered anyway.
I have a very complex json structure. It contains many array elements and those array elements contains other array elements and so on..
Please see below json tree structure.
Json Tree Structure-1 :
Json Tree Structure-2 :
As highlighted above in yellow, I want to update the value of "rdKey" field.
I wrote below code and it is perfectly working fine :
String json = "escaped string (as it's a big string, I can't put it here)";
JSONObject jsonObj = new JSONObject(json);
if (jsonObj.has("responseMap")) {
JSONObject responseMap = jsonObj.getJSONObject("responseMap");
if (responseMap.has("ValueJson")) {
JSONObject valueJson = responseMap.getJSONObject("ValueJson");
if (valueJson.has("ticketBean_CM")) {
JSONObject ticketBean_CM = valueJson.getJSONObject("ticketBean_CM");
if (ticketBean_CM.has("addByGamma")) {
String addByGamma = ticketBean_CM.getString("addByGamma");
System.out.println(addByGamma);
if (addByGamma.equals("VCE")) {
if (responseMap.has("ScreenJson")) {
JSONObject screenJson = responseMap.getJSONObject("ScreenJson");
if (screenJson.has("sections")) {
JSONArray sectionArray1 = screenJson.getJSONArray("sections");
if (sectionArray1.length() > 0) {
JSONObject section0 = sectionArray1.getJSONObject(0);
if (section0.has("sections")) {
JSONArray sectionArray2 = section0.getJSONArray("sections");
if (sectionArray2.length() > 3) {
JSONObject section6 = sectionArray2.getJSONObject(3);
if (section6.has("sections")) {
JSONArray sectionArray3 = section6.getJSONArray("sections");
if (sectionArray3.length() > 1) {
JSONObject section8 = sectionArray3.getJSONObject(1);
if (section8.has("elements")) {
JSONArray elementsArray1 = section8
.getJSONArray("elements");
if (elementsArray1.length() > 0) {
JSONObject elements1 = elementsArray1.getJSONObject(0);
if (elements1.has("elements")) {
JSONArray elementsArray2 = elements1
.getJSONArray("elements");
if (elementsArray2.length() > 4) {
JSONObject elements2 = elementsArray2
.getJSONObject(4);
if (elements2.has("rdKey")) {
System.out.println(
elements2.getString("rdKey"));
elements2.put("rdKey",
"CircuitID(FullPartial)");
System.out.println(
elements2.getString("rdKey"));
System.out.println(jsonObj.toString());
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
I want you guys to help me if there is any better solution for this. Can I do it without traversing the entire json object (till I find the concerned field) ? This solution will not work if json tree structure gets changes, it needs to be static as a success scenario of this code.
Please suggest better solution.
If you want to escape traversing of JSON then you can use JSONPointer, available in same org.json library.
E.g.:
String query = <json_pointer_query to element array>
JSONPointer pointer = new JSONPointer(query);
JSONObject elementsArrayJSON = (JSONObject) pointer.queryFrom(jsonObj);
elementsArrayJSON.put("rdKey","CircuitID(FullPartial)");
JSON Pointer query language can be referred in:
https://www.rfc-editor.org/rfc/rfc6901
Note:
JSON Pointer is pretty basic, it doesn't support wild card. So you need to be sure about element names, otherwise it would throw exception.
If you're flexible on what library to use, maybe the JsonPath will be useful for you.
You can update all "elements" with "rdKey" using the following code:
JsonPath.parse(json).set("$..elements[?(#.rdKey)].rdKey", "CircuitID(FullPartial)").json()
This question already has answers here:
How to parse JSON in Java
(36 answers)
Closed 4 years ago.
I have JSON object as follows:
member = "{interests : [{interestKey:Dogs}, {interestKey:Cats}]}";
In Java I want to parse the above json object and store the values in an arraylist.
I am seeking some code through which I can achieve this.
I'm assuming you want to store the interestKeys in a list.
Using the org.json library:
JSONObject obj = new JSONObject("{interests : [{interestKey:Dogs}, {interestKey:Cats}]}");
List<String> list = new ArrayList<String>();
JSONArray array = obj.getJSONArray("interests");
for(int i = 0 ; i < array.length() ; i++){
list.add(array.getJSONObject(i).getString("interestKey"));
}
public class JsonParsing {
public static Properties properties = null;
public static JSONObject jsonObject = null;
static {
properties = new Properties();
}
public static void main(String[] args) {
try {
JSONParser jsonParser = new JSONParser();
File file = new File("src/main/java/read.json");
Object object = jsonParser.parse(new FileReader(file));
jsonObject = (JSONObject) object;
parseJson(jsonObject);
} catch (Exception ex) {
ex.printStackTrace();
}
}
public static void getArray(Object object2) throws ParseException {
JSONArray jsonArr = (JSONArray) object2;
for (int k = 0; k < jsonArr.size(); k++) {
if (jsonArr.get(k) instanceof JSONObject) {
parseJson((JSONObject) jsonArr.get(k));
} else {
System.out.println(jsonArr.get(k));
}
}
}
public static void parseJson(JSONObject jsonObject) throws ParseException {
Set<Object> set = jsonObject.keySet();
Iterator<Object> iterator = set.iterator();
while (iterator.hasNext()) {
Object obj = iterator.next();
if (jsonObject.get(obj) instanceof JSONArray) {
System.out.println(obj.toString());
getArray(jsonObject.get(obj));
} else {
if (jsonObject.get(obj) instanceof JSONObject) {
parseJson((JSONObject) jsonObject.get(obj));
} else {
System.out.println(obj.toString() + "\t"
+ jsonObject.get(obj));
}
}
}
}}
Thank you so much to #Code in another answer. I can read any JSON file thanks to your code. Now, I'm trying to organize all the elements by levels, for could use them!
I was working with Android reading a JSON from an URL and the only I had to change was the lines
Set<Object> set = jsonObject.keySet();
Iterator<Object> iterator = set.iterator();
for
Iterator<?> iterator = jsonObject.keys();
I share my implementation, to help someone:
public void parseJson(JSONObject jsonObject) throws ParseException, JSONException {
Iterator<?> iterator = jsonObject.keys();
while (iterator.hasNext()) {
String obj = iterator.next().toString();
if (jsonObject.get(obj) instanceof JSONArray) {
//Toast.makeText(MainActivity.this, "Objeto: JSONArray", Toast.LENGTH_SHORT).show();
//System.out.println(obj.toString());
TextView txtView = new TextView(this);
txtView.setText(obj.toString());
layoutIzq.addView(txtView);
getArray(jsonObject.get(obj));
} else {
if (jsonObject.get(obj) instanceof JSONObject) {
//Toast.makeText(MainActivity.this, "Objeto: JSONObject", Toast.LENGTH_SHORT).show();
parseJson((JSONObject) jsonObject.get(obj));
} else {
//Toast.makeText(MainActivity.this, "Objeto: Value", Toast.LENGTH_SHORT).show();
//System.out.println(obj.toString() + "\t"+ jsonObject.get(obj));
TextView txtView = new TextView(this);
txtView.setText(obj.toString() + "\t"+ jsonObject.get(obj));
layoutIzq.addView(txtView);
}
}
}
}
1.) Create an arraylist of appropriate type, in this case i.e String
2.) Create a JSONObject while passing your string to JSONObject constructor as input
As JSONObject notation is represented by braces i.e {}
Where as JSONArray notation is represented by square brackets i.e []
3.) Retrieve JSONArray from JSONObject (created at 2nd step) using "interests" as index.
4.) Traverse JASONArray using loops upto the length of array provided by length() function
5.) Retrieve your JSONObjects from JSONArray using getJSONObject(index) function
6.) Fetch the data from JSONObject using index '"interestKey"'.
Note : JSON parsing uses the escape sequence for special nested characters if the json response (usually from other JSON response APIs) contains quotes (") like this
`"{"key":"value"}"`
should be like this
`"{\"key\":\"value\"}"`
so you can use JSONParser to achieve escaped sequence format for safety as
JSONParser parser = new JSONParser();
JSONObject json = (JSONObject) parser.parse(inputString);
Code :
JSONParser parser = new JSONParser();
String response = "{interests : [{interestKey:Dogs}, {interestKey:Cats}]}";
JSONObject jsonObj = (JSONObject) parser.parse(response);
or
JSONObject jsonObj = new JSONObject("{interests : [{interestKey:Dogs}, {interestKey:Cats}]}");
List<String> interestList = new ArrayList<String>();
JSONArray jsonArray = jsonObj.getJSONArray("interests");
for(int i = 0 ; i < jsonArray.length() ; i++){
interestList.add(jsonArray.getJSONObject(i).optString("interestKey"));
}
Note : Sometime you may see some exceptions when the values are not available in appropriate type or is there is no mapping key so in those cases when you are not sure about the presence of value so use optString, optInt, optBoolean etc which will simply return the default value if it is not present and even try to convert value to int if it is of string type and vice-versa so Simply No null or NumberFormat exceptions at all in case of missing key or value
From docs
Get an optional string associated with a key. It returns the
defaultValue if there is no such key.
public String optString(String key, String defaultValue) {
String missingKeyValue = json_data.optString("status","N/A");
// note there is no such key as "status" in response
// will return "N/A" if no key found
or To get empty string i.e "" if no key found then simply use
String missingKeyValue = json_data.optString("status");
// will return "" if no key found where "" is an empty string
Further reference to study
How to convert String to JSONObject in Java
Convert one array list item into multiple Items
There are many JSON libraries available in Java.
The most notorious ones are: Jackson, GSON, Genson, FastJson and org.json.
There are typically three things one should look at for choosing any library:
Performance
Ease of use (code is simple to write and legible) - that goes with features.
For mobile apps: dependency/jar size
Specifically for JSON libraries (and any serialization/deserialization libs), databinding is also usually of interest as it removes the need of writing boiler-plate code to pack/unpack the data.
For 1, see this benchmark: https://github.com/fabienrenaud/java-json-benchmark I did using JMH which compares (jackson, gson, genson, fastjson, org.json, jsonp) performance of serializers and deserializers using stream and databind APIs.
For 2, you can find numerous examples on the Internet. The benchmark above can also be used as a source of examples...
Quick takeaway of the benchmark: Jackson performs 5 to 6 times better than org.json and more than twice better than GSON.
For your particular example, the following code decodes your json with jackson:
public class MyObj {
private List<Interest> interests;
static final class Interest {
private String interestKey;
}
private static final ObjectMapper MAPPER = new ObjectMapper();
public static void main(String[] args) throws IOException {
MyObj o = JACKSON.readValue("{\"interests\": [{\"interestKey\": \"Dogs\"}, {\"interestKey\": \"Cats\" }]}", MyObj.class);
}
}
Let me know if you have any questions.