12

I had a need for having Key/Value pairs in the order of my insertion, so I opted to use LinkedHashMap over HashMap. But I need to convert the LinkedHashMap into a JSON String where the order in the LinkedHashMap is maintained in the string.

But currently I'm achieving it by:

  1. First converting the LinkedHashMap into JSON.
  2. Then converting the JSON into a string.

    import java.util.LinkedHashMap;
    import java.util.Map;
    
    import org.json.JSONObject;
    
    public class cdf {
        public static void main(String[] args) {
            Map<String,String > myLinkedHashMap =  new LinkedHashMap<String, String>();
            myLinkedHashMap.put("1","first");
            myLinkedHashMap.put("2","second");
            myLinkedHashMap.put("3","third");
    
            JSONObject json = new JSONObject(myLinkedHashMap);
            System.out.println(json.toString());
        }
    }
    

The output is:

{"3":"third","2":"second","1":"first"} . 

But I want it in the order of insertion of the keys, like this:

{"1":"first","2":"second","3":"third"}

Once I convert the LinkedHashMap into a JSON it loses it order (it's obvious that JSON doesn't have the notion of order) and hence the string too is out of order. Now, how do I generate a JSON string whose order is same as the LinkedHashMap?

Mr. Polywhirl
  • 42,981
  • 12
  • 84
  • 132
Karthic Rao
  • 3,624
  • 8
  • 30
  • 44
  • 1
    It would *really* help if you'd show us this in a short but complete program demonstrating the problem... (Code, expected output, actual output.) – Jon Skeet Apr 07 '15 at 12:13
  • You can add an element `order` to each JSON object so you are able to track the string ordering after receiving and parsing the JSON. – Bruno Toffolo Apr 07 '15 at 12:13
  • @JonSkeet: I have edited the question now ..Hope this is clear enough . – Karthic Rao Apr 07 '15 at 12:23
  • Hmm. The output on my machine is just `{}` - but then it didn't compile to start with anyway. Please update the question with *actual* code you've compiled and run... – Jon Skeet Apr 07 '15 at 12:26
  • @MrPolywhirl: I've rolled back to revision 4 as although the text may not be as good, it at least includes a (flawed) example... – Jon Skeet Apr 07 '15 at 12:27
  • @KarthicRao: I have heard that Gson may keep your ordering when converting to a String. Check it out, it was made by Google, so you know it works ;-) – Mr. Polywhirl Apr 07 '15 at 12:31
  • @JonSkeet: I believe my intent is clear atleast now with the edited piece of code ! – Karthic Rao Apr 07 '15 at 12:44
  • Yes, but the code you've given doesn't give the output you've reported - at least for me. Will try downloading a later version of the JSON library... – Jon Skeet Apr 07 '15 at 12:45
  • Ah - if you change it to a `Map` *then* the output is right... – Jon Skeet Apr 07 '15 at 12:48
  • @JonSkeet: You can generalize this question to be "How to obtain a JSON string from a LinkedHashMap where the order of the LinkedHashMap is maintained in the Json string " – Karthic Rao Apr 07 '15 at 12:51
  • My point is that you should be providing sample code which actually produces the output you claim it does. – Jon Skeet Apr 07 '15 at 12:51

7 Answers7

18

Gson if your friend. This will print the ordered map into an ordered JSON string.

If you want to preserve insertion order, use a LinkedHashMap.

I used the latest version of Gson (2.8.5), you can can download it via the following options at the bottom of this post.

import java.util.*;
import com.google.gson.Gson;

public class OrderedJson {
    public static void main(String[] args) {
        // Create a new ordered map.
        Map<String,String> myLinkedHashMap = new LinkedHashMap<String, String>();

        // Add items, in-order, to the map.
        myLinkedHashMap.put("1", "first");
        myLinkedHashMap.put("2", "second");
        myLinkedHashMap.put("3", "third");

        // Instantiate a new Gson instance.
        Gson gson = new Gson();

        // Convert the ordered map into an ordered string.
        String json = gson.toJson(myLinkedHashMap, LinkedHashMap.class);

        // Print ordered string.
        System.out.println(json); // {"1":"first","2":"second","3":"third"}
    }
}

If you want the items to always be inserted at the right place, use a TreeMap instead.

import java.util.*;
import com.google.gson.Gson;

public class OrderedJson {
    public static void main(String[] args) {
        // Create a new ordered map.
        Map<String,String> myTreeHashMap = new TreeMap<String, String>();

        // Add items, in any order, to the map.
        myTreeHashMap.put("3", "third");
        myTreeHashMap.put("1", "first");
        myTreeHashMap.put("2", "second");

        // Instantiate a new Gson instance.
        Gson gson = new Gson();

        // Convert the ordered map into an ordered string.
        String json = gson.toJson(myTreeHashMap, TreeMap.class);

        // Print ordered string.
        System.out.println(json); // {"1":"first","2":"second","3":"third"}
    }
}

Dependency Options

Maven

<dependency>
    <groupId>com.google.code.gson</groupId>
    <artifactId>gson</artifactId>
    <version>2.8.5</version>
</dependency>

Gradle

compile 'com.google.code.gson:gson:2.8.5'

Or you can visit Maven Central for more download options.

Mr. Polywhirl
  • 42,981
  • 12
  • 84
  • 132
  • 4
    Thanks a lot :) It works :) Thank you for the Maven Dependency too , It helped :) – Karthic Rao Apr 07 '15 at 12:59
  • 2
    very useful . . . :) – msoa Jun 15 '16 at 20:09
  • 1
    It's incredible lame that Jackson can't do this. – goat Dec 07 '17 at 01:30
  • 1
    @goat Jackson does it just fine, see my answer. OP just complicated code unnecessarily... something GSON example above also rectified. – StaxMan May 07 '18 at 18:33
  • This example doesn't work because GSON uses a TreeMap, so the right example would be to do myLinkedHashMap.put("3", "third"); and myLinkedHashMap.put("1", "one"), so you would see that GSON flips the results.. – Rafael Sanches Mar 11 '19 at 17:20
  • @RafaelSanches I updated the response to include `TreeMap`, but do not down-vote me for answering correctly; regarding the use of a `LinkedHashMap`. – Mr. Polywhirl Mar 12 '19 at 11:01
6

JSONObject implies use of org.json library. Don't do that; it is old prototype and there are better options like Jackson and GSON. With Jackson you would just use:

String json = new ObjectMapper().writeValueAsString(myLinkedHashMap);

and get JSON string with entries in whatever traversal order your Map uses.

StaxMan
  • 113,358
  • 34
  • 211
  • 239
1

JSON not taking insertion order due to linkedhashmap params both are string. Is it fine to change first param as Integer like below mentioned code:

Map<Integer,String > myLinkedHashMap =  new LinkedHashMap<>();
            myLinkedHashMap.put(1,"first");
            myLinkedHashMap.put(2,"second");
            myLinkedHashMap.put(3,"third");

            System.out.println(myLinkedHashMap);
            JSONObject json = new JSONObject(myLinkedHashMap);
            System.out.println(json.toString());
Subbu
  • 308
  • 2
  • 4
  • 12
  • But how to convert into a one JSON string with the order maintained? – Karthic Rao Apr 07 '15 at 12:39
  • My suggested code is ordered maintained. Not able to understanding your question. – Subbu Apr 07 '15 at 12:46
  • 1
    No, this won't do it - the problem is that `JSONObject` uses a plain `HashMap` behind the scenes, and you can't change that :( – Jon Skeet Apr 07 '15 at 12:52
  • @Jon Skeet - What is the wrong in this? is this output wrong? am getting output for above program: {1=first, 2=second, 3=third} {"1":"first","2":"second","3":"third"} – Subbu Apr 07 '15 at 12:54
  • @Subbu: That's coincidence rather than a guarantee, I suspect. (You're still relying on the ordering of a `HashMap`, which is a really bad idea.) – Jon Skeet Apr 07 '15 at 12:59
  • Mr @JonSkeet is right , There is no guarantee on ordering of a HashMap , its just a coincidence that its turning out to be right . What i wanted was a guaranteed ordering on a Hashmap with large number of Key/value pairs in it . – Karthic Rao Apr 07 '15 at 14:12
0

To sort alphabetically, here is the proper way with Jackson 2.*:

Map<String, String> myLinkedHashMap = new LinkedHashMap<String, String>();
myLinkedHashMap.put("1", "first");
myLinkedHashMap.put("2", "second");
myLinkedHashMap.put("3", "third");

ObjectMapper mapper = new ObjectMapper();
mapper.configure(MapperFeature.SORT_PROPERTIES_ALPHABETICALLY, true);

try {
    System.out.println(mapper.writeValueAsString(myLinkedHashMap));
    //prints {"1":"first","2":"second","3":"third"}
} catch (JsonProcessingException e) {
    e.printStackTrace();
}
mtyurt
  • 3,369
  • 6
  • 38
  • 57
  • Alternatively for sorting one can use `TreeMap` as well. Serializer uses traversal order of the `Map`. I am not quite sure why answers claim Jackson does not preserve ordering: it does. – StaxMan May 07 '18 at 19:01
0

I ran into the same problem. I had to use this particular library, and the first element must be with the key "$type". Here is my decision:

import org.json.JSONObject;

import java.util.HashMap;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Set;


class TypedJSONObject extends JSONObject {

    private static final String KEY = "$type";

    @Override
    public Set<Map.Entry<String, Object>> entrySet() {
        Set<Map.Entry<String, Object>> superSet = super.entrySet();
        Set<Map.Entry<String, Object>> returnSet = new LinkedHashSet<>();

        HashMap<String, Object> map = new HashMap<>();
        for (Map.Entry<String, Object> entry : superSet) {
            if (entry.getKey().equals(KEY)) {
                map.put(KEY, entry.getValue());
                break;
            }
        }
        Set<Map.Entry<String, Object>> entries = map.entrySet();

        returnSet.addAll(entries);
        returnSet.addAll(superSet);

        return returnSet;
    }
}

As you see I overrite method entrySet, that used in method toString

0

i created a hacky solution, which should not be used for production, i just use it to manualy generate json files

import java.lang.reflect.Field;
import java.util.LinkedHashMap;

import org.json.JSONObject;

public class SortedJSONObject extends JSONObject {

    public SortedJSONObject() {
        super();
        try {
            Field mapField = JSONObject.class.getDeclaredField("map");
            mapField.setAccessible(true);
            mapField.set(this, new LinkedHashMap());
            mapField.setAccessible(false);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

}
wutzebaer
  • 14,365
  • 19
  • 99
  • 170
-1

Create JsonArray for Ordered Pair...

JSONArray orderedJson=new JSONArray();
Iterator iterator= ;
    while (iterator.hasNext()) {
        Map.Entry me = (Map.Entry)iteratornext();
        System.out.print(me.getKey() + ": ");
        System.out.println(me.getValue());
        JSONObject tmpJson=new JSONObject ();
        tmpJson.put(me.getKey(),me.getValue());//add key/value to tmpjson
        orderedJson.add(tmpJson);//add tmpjson to orderedJson
    }
Angad Tiwari
  • 1,738
  • 1
  • 12
  • 23