JSON order mixed up

Viewed 159591

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!!

18 Answers

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.

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!!!

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.

ES6 You can do it in this way . The order will be kept intact

 var Sorted=[];
 Sorted.push({test1:check1})
 Sorted.push({test2:check2})
 Sorted.push({test3:check3})

It works because we are pushing the key value pairs one after another. In this way we can reliably achieve the ordering goal.

Related