lannyf
lannyf

Reputation: 11035

How to get key from the json and preserve the order

I have a JSON with some key-value pairs. I need to pick up by name and process it's value differently. The oder of the process should be same order as the keys listed in the JSON (JSON is unknown, only knows it will have some key and corresponding value, so cant not know the order outside of the JSON).

Simplified sample usage:

String jsonData = "{key1:{some other data}, key2:{sub-key:sub-value}}";
val jsonObject = JSONObject(jsonData)
val names = jsonObject.keys()
if (names.hasNext()) {
 val key = names.next()
 val valueJson = jsonData.optString(key)
 val gson = Gson()

 val resultList = ArrayList<Data> () // result should keep the order as the keys in the json
 if (key == "key1") {
  val parsedResult = gson.fromJson <POJO_1> (valueJson, POJO_1::class.java)
  resultList.add(processDataFromKey1(parsedResult))
 } else if (key == "key2") {
  val parsedResult = gson.fromJson <POJO_2> (valueJson, POJO_2::class.java)
  resultList.add(processDataFromKey2(parsedResult))
 }
}

The problem is the jsonObject.keys() the order of the keys is undefined.

So how to preserve the order of the keys from the JSON string (don't want to add dependency javax.json:javax.json-api)

Upvotes: 0

Views: 2444

Answers (3)

Lorenzo
Lorenzo

Reputation: 61

Another thing you can do is refer to the String jsonData in your example and rebuild the order.

Something like:

JSONObject json = new JSONObject(jsonData);
Iterator<String> keys = json.keys();
List<KeyOrder> ordering = new ArrayList();
while(keys.hasNext()) {
    String key = keys.next();
    int loc = jsonData.indexOf('"' + key + '"');
    ordering.add(new KeyOrder(loc, key));
}
Collections.sort(ordering);
for(KeyOrder order: ordering) {
    Object value = json.get(order.getKey());
    // process the key and value
}

public class KeyOrder implements Comparable<KeyOrder> {
    private int order;
    private String key;

    public KeyOrder(int order, String key) {
        this.order = order;
        this.key = key;
    }

    @Override
    public int compareTo(KeyOrder o) {
        return order - o.getOrder();
    }

    private int getOrder() {
        return order;
    }

    public String getKey() {
        return key;
    }
}

Upvotes: 0

Michał Ziober
Michał Ziober

Reputation: 38690

org.json.JSONObject internally uses HashMap. They even added some comment to it:

HashMap is used on purpose to ensure that elements are unordered by the specification. JSON tends to be a portable transfer format to allows the container implementations to rearrange their items for a faster element retrieval based on associative access. Therefore, an implementation mustn't rely on the order of the item.

Use Gson. It uses com.google.gson.internal.LinkedTreeMap implementation for Map. From it's documentation:

A map of comparable keys to values. Unlike TreeMap, this class uses insertion order for iteration order. Comparison order is only used as an optimization for efficient insertion and removal.
This implementation was derived from Android 4.1's TreeMap class.

So, for below JSON payload:

{
  "status": "status",
  "date": "01/10/2019",
  "abc": {
    "Field1": "value1",
    "key": "value2"
  },
  "rgj": {
    "key": "value2",
    "Field1": "value3"
  },
  "name": "Rick"
}

This example app:

import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.reflect.TypeToken;

import java.io.File;
import java.io.FileReader;
import java.lang.reflect.Type;
import java.util.Map;

public class GsonApp {

    public static void main(String[] args) throws Exception {
        File jsonFile = new File("./resource/test.json").getAbsoluteFile();
        Gson gson = new GsonBuilder().create();

        Type type = new TypeToken<Map<String, Object>>() {
        }.getType();
        Map<String, Object> map = gson.fromJson(new FileReader(jsonFile), type);
        map.forEach((k, v) -> {
            if (v instanceof Map) {
                System.out.println(k);
                ((Map) v).forEach((k1, v1) -> System.out.println("   " + k1 + " => " + v1));
            } else {
                System.out.println(k + " => " + v);
            }
        });
    }
}

Prints:

status => status
date => 01/10/2019
abc
   Field1 => value1
   key => value2
rgj
   key => value2
   Field1 => value3
name => Rick

Notice that key and Field1 are in different order in abc and rgj objects.

Upvotes: 2

cghislai
cghislai

Reputation: 1801

you could use jsonparser rather than jsonreader so you control the event flow. See https://docs.oracle.com/javaee/7/api/javax/json/stream/JsonParser.html

Note that your question is neither kotlin nor gson specific.

Upvotes: 1

Related Questions