84

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

Jon Bates
  • 3,055
  • 2
  • 30
  • 48
JSON guy
  • 849
  • 1
  • 6
  • 3
  • Is this using the `org.json` stuff? – skaffman Oct 16 '10 at 09:07
  • possible duplicate of [JSONObject : Why JSONObject changing the order of attributes](http://stackoverflow.com/questions/17229418/jsonobject-why-jsonobject-changing-the-order-of-attributes) – Leo Jan 26 '14 at 13:19
  • 6
    @Leo This is a possible duplicate of a question that was posted three years later and has an answer that links back to this one? If anything, the other question should be closed as a dupe. – Adi Inbar Jan 26 '14 at 21:47
  • do you want me to go to the other question and mark it as dupe instead? – Leo Jan 27 '14 at 01:00
  • 2
    Here's my problem... I understand that JSON doesn't have an order and a library is free to generate any order it feels like, but there's a definite human element here. When looking through JSON, maybe to visually check everything, it can be quite difficult to see problems when you expect one order and the library generates another. Sure, as people have suggested, there are work-arounds etc, but when I create JSON, my brain is thinking about it in order and it's hard when its emitted in a differing order. I think a library should use the order you specify, even if a parser doesn't care. – The Welder Sep 19 '18 at 05:07
  • Depending on the library you are using (I use simplejson 1.1) you can replace JSONObject with LinkedHashMap – Yaza Nov 01 '20 at 19:01

17 Answers17

120

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.

samabcde
  • 6,988
  • 2
  • 25
  • 41
Adrian Smith
  • 17,236
  • 11
  • 71
  • 93
  • 13
    **_As a consequence, JSON libraries are free to rearrange the order of the elements as they see fit. This is not a bug._** Just curious to know, what is be the benefit in re-arranging elements. Thanks, durai. – durai Jul 11 '12 at 12:57
  • 12
    @durai: Some associative containers use a sorting function to arrange their items and thus do not preserve ordering to allow faster element retrieval. – ereOn Oct 29 '12 at 18:27
  • Well, they state the following here (if this is indeed gson). http://google-gson.googlecode.com/svn/tags/1.2.3/docs/javadocs/com/google/gson/JsonObject.html "The member elements of this object are maintained in order they were added." – Ted Aug 05 '14 at 14:47
  • Hey, people are referring to this! But arrays have an order, so items in an array should not be shuffled. – Thomas Weller Sep 22 '15 at 15:09
  • 3
    @Ted that's GSON, *a Java library* developed by Google for handling JSON. It's up to each library developer if they want to reorder the field or not. – Andrew T. Oct 20 '15 at 08:12
  • 4
    @Thomas This is about JSON Object, not JSON Array – Daniel Higueras Feb 01 '16 at 11:29
  • explained quite well [here](https://stackoverflow.com/a/7214316/4288043) – cardamom Jan 31 '19 at 16:33
  • The SugarCRM RestApi, requires login details shared in specific order, [this](https://towardsdatascience.com/create-an-ordered-jsonobject-in-java-fb9629247d76) came handy – Glitch Aug 10 '21 at 20:14
17

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

lemiorhan
  • 1,434
  • 11
  • 18
  • 3
    This solution is not work for me. Conversion to JSONObject is throwing exception. If I construct JSONObject(map) then order is not preserved. If i leave assignment without conversion then string is assigned instead of object. – Ernest Poletaev Dec 25 '14 at 09:00
  • 1
    This worked for me, but I had to use `JSONObject.toJSONString(obj)`. Otherwise, I was getting a conversion error as @Ernest mentioned above. – Tricky12 Jul 22 '15 at 17:44
  • @Tricky12 This makes no sense to me: 1.) `org.json.JSONObject` does not have this method. 2.) The method which seems like it could do the trick `org.json.JSONObject.valueToString(obj)` does NOT work, since it does this internally: `new JSONObject(map).toString()` which again uses `HashMap` and not `LinkedHashMap` inside: `this.map = new HashMap();` – Frederic Leitenberger Oct 27 '17 at 10:13
  • @lemiorhan: What (language) is this? It looks a little bit like `Java` but it won't work in `Java` for several reasons: Apart from the obvious syntax errors like missing ";", the keyword "def" does not exist in Java and finally: `JSONObject json = (JSONObject) obj` → A `HashMap` can not be cast to `JSONObject` → `ClassCastException`. And one more thing: The link at the end of your post leads to an ad-page (broken link?). – Frederic Leitenberger Oct 27 '17 at 11:44
  • 1
    @FredericLeitenberger `toJSONString()` is a method if you have library `org.json.simple`. This was available to me in my IDE. I haven't looked at this for a couple of years now, so I'm not sure how else to help. – Tricky12 Oct 27 '17 at 19:26
  • 2
    @FredericLeitenberger I believe it is Groovy, a scripting language that is based on java. – Turtle Jan 04 '18 at 11:18
  • in android should change `JSONObject json = (JSONObject) obj` to `JSONObject json = new JSONObject(obj);` – Saeid Feb 25 '18 at 21:12
9

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

thyzz
  • 683
  • 1
  • 9
  • 14
6

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;
    }
}
UnixShadow
  • 1,222
  • 8
  • 12
  • Nice ~hack~ solution. In my version of `JSONObject` the `map` field is final, but it seems to work nevertheless. If it does not work this addition should help to overcome the `final`: https://stackoverflow.com/a/3301720/1520422 – Frederic Leitenberger Oct 27 '17 at 12:03
4

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());
sang
  • 49
  • 1
  • 3
3

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.

Mark Snidovich
  • 1,055
  • 7
  • 11
  • Well, they state the following here (if this is indeed gson). http://google-gson.googlecode.com/svn/tags/1.2.3/docs/javadocs/com/google/gson/JsonObject.html "The member elements of this object are maintained in order they were added." – Ted Aug 05 '14 at 14:46
  • 1
    @Ted that's *gson*, not *json*. – Madbreaks Sep 21 '18 at 20:53
2

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

Martijn Pieters
  • 1,048,767
  • 296
  • 4,058
  • 3,343
Dhina k
  • 1,481
  • 18
  • 24
1

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.

tsohr
  • 865
  • 2
  • 15
  • 25
0

As all are telling you, JSON does not maintain "sequence" but array does, maybe this could convince you: Ordered JSONObject

Community
  • 1
  • 1
Kartikya
  • 455
  • 2
  • 9
0

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.}}

prachi
  • 1
  • 3
0

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"}

  • Sorry, this is a terrible suggestion. Soo much potential for breakage depending ho what your keys/values contain. In this day and age, rolling your own JSON logic is a bad idea. – Madbreaks Sep 21 '18 at 20:30
  • @Madbreaks, I'm not trying to rollout any JSON logic. If the requirement is to have an ordered json then the easier approach would be to convert the linkedHashMap into a json string. The logic would differ for lists (as in JsonArray). Depending on the usage, encoding can also be included. – Joy Banerjee Nov 19 '18 at 09:33
0

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));
Valentyn Kolesnikov
  • 2,029
  • 1
  • 24
  • 31
0

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)
t3az0r
  • 449
  • 4
  • 8
0

Just add the order with this tag

@JsonPropertyOrder({ "property1", "property2"})
Dharman
  • 30,962
  • 25
  • 85
  • 135
0

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);
    }
}
BATMAN_2008
  • 2,788
  • 3
  • 31
  • 98
  • But how do you use this method to get data from a json text file and maintain the order in the file? – Hasen Oct 22 '21 at 07:23
0

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>
JRichardsz
  • 14,356
  • 6
  • 59
  • 94
0

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.

Renetik
  • 5,887
  • 1
  • 47
  • 66