Groovy JsonSlurper is a useful tool to parse JSON strings. For a JSON object, the parsing result is a Map object. In certain cases, we want to keep the iteration order of Map properties same as encounter order in original JSON string.

By default, JsonSlurper uses a TreeMap, so the properties are actually sorted. Given following program, the result will be obj => {a=0, x=2, z=1}.

public class Test {  
    public static void main(String[] args) {
        String jsonString = "{\"obj\": {\"a\": 0, \"z\": 1, \"x\": 2}}";
        JsonSlurper jsonSlurper = new JsonSlurper();
        Map map = (Map) jsonSlurper.parseText(jsonString);
        map.forEach((k, v) -> System.out.println(String.format("%s => %s", k, v)));
    }
}

To keep the original properties ordering, you can add -Djdk.map.althashing.threshold=512 as the JVM argument, then the output will be obj => {a=0, z=1, x=2}.

The reason is in the source code of groovy.json.internal.LazyMap used by JsonSlurper. See in GitHub. So If jdk.map.althashing.threshold system property is set, LazyMap will use a LinkedHashMap implementation instead of TreeMap, then it will keep the properties ordering.

private static final String JDK_MAP_ALTHASHING_SYSPROP = System.getProperty("jdk.map.althashing.threshold");

private void buildIfNeeded() {  
   if (map == null) {
        /** added to avoid hash collision attack. */
        if (Sys.is1_7OrLater() && JDK_MAP_ALTHASHING_SYSPROP != null) {
            map = new LinkedHashMap<String, Object>(size, 0.01f);
        } else {
            map = new TreeMap<String, Object>();
        }

        for (int index = 0; index < size; index++) {
            map.put(keys[index], values[index]);
        }
        this.keys = null;
        this.values = null;
    }
}

Please note this solution should be used as a hack as it depends on Groovy's internal implementation details. So this behavior may change in future version of Groovy.

Note for Java 8

jdk.map.althashing.threshold system property is removed in Java SE 8, but this hack still works in Java 8 as the implementation only checks the existence of this system property, but not actually uses it.