Search code examples
javajsonstringkey-valuealphabetical

Convert JSON to a sorted JSON which include JSONArrays in nested nodes


Here I would like to know that, how to convert a un-sorted JSON string to a sorted JSON string which include multiple JSON arrays as child elements. The sample un-sorted JSON string is given below,

{
    "name": "John",
    "age": 22,
    "foods": [{
            "product": "apple",
            "price": 100
        }, {
            "fruit": "banana",
            "price": 100
        }
    ],

    "attributes": {
        "OBJECTID": "35",
        "FACILITYTYPE": "Pharmacy",
        "FACILITYSUBTYPE": "24 Hr Pharmacy",
        "COMMERCIALNAME_E": "SADD MAARAB PHARMACY"
    }
}

Expected output is as follows,

{
    "age": 22,
    "attributes": {
        "COMMERCIALNAME_E": "SADD MAARAB PHARMACY",
        "FACILITYSUBTYPE": "24 Hr Pharmacy",
        "FACILITYTYPE": "Pharmacy",
        "OBJECTID": "35"
    },
    "foods": [{
            "price": 100m
            "product": "apple"
        }, {
            "fruit": "banana",
            "price": 100
        }
    ],
    "name": "John",
}

Can someone help me to achieve this requirement?


Solution

  • JSON Object in Java can be represented by Map<String, Object> and JSON Array can be represented by List, Set, array[] or any other Collection. Most popular libraries like Jackson and Gson can do that by default. For JSON Object documentation says:

    An object is an unordered set of name/value pairs.

    So, most libraries by default do not take care about order and used unordered Map implementations. At most they can keep order from JSON payload by using LinkedHashMap or any other similar implementation. But to force order we need to customise configuration.

    For Gson it can look like this:

    import com.google.gson.Gson;
    import com.google.gson.GsonBuilder;
    import com.google.gson.TypeAdapter;
    import com.google.gson.TypeAdapterFactory;
    import com.google.gson.reflect.TypeToken;
    import com.google.gson.stream.JsonReader;
    import com.google.gson.stream.JsonWriter;
    
    import java.io.File;
    import java.io.FileReader;
    import java.io.IOException;
    import java.util.Map;
    import java.util.TreeMap;
    
    public class GsonApp {
    
        public static void main(String[] args) throws Exception {
            File jsonFile = new File("./resource/test.json").getAbsoluteFile();
    
            Gson gson = new GsonBuilder()
                    .setPrettyPrinting()
                    .registerTypeAdapterFactory(new TreeMapTypeAdapterFactory())
                    .create();
    
            Map root = gson.fromJson(new FileReader(jsonFile), Map.class);
            System.out.println(gson.toJson(root));
        }
    }
    
    class TreeMapTypeAdapterFactory implements TypeAdapterFactory {
    
        public final <T> TypeAdapter<T> create(Gson gson, TypeToken<T> type) {
            if (Map.class.isAssignableFrom(type.getRawType())) {
                final TypeAdapter<T> delegate = gson.getDelegateAdapter(this, type);
                return createCustomTypeAdapter(delegate);
            }
    
            return null;
        }
    
        private <T> TypeAdapter<T> createCustomTypeAdapter(TypeAdapter<T> delegate) {
            return new TypeAdapter<T>() {
                @Override
                public void write(JsonWriter out, T value) throws IOException {
                    Map map = (Map) value;
                    delegate.write(out, (T) new TreeMap(map));
                }
    
                @Override
                public T read(JsonReader in) throws IOException {
                    return delegate.read(in);
                }
            };
        }
    }
    

    For JSON payload above app prints:

    {
      "age": 22.0,
      "attributes": {
        "COMMERCIALNAME_E": "SADD MAARAB PHARMACY",
        "FACILITYSUBTYPE": "24 Hr Pharmacy",
        "FACILITYTYPE": "Pharmacy",
        "OBJECTID": "35"
      },
      "foods": [
        {
          "price": 100.0,
          "product": "apple"
        },
        {
          "fruit": "banana",
          "price": 100.0
        }
      ],
      "name": "John"
    }