diff --git a/config/src/main/java/com/typesafe/config/impl/SimpleConfigObject.java b/config/src/main/java/com/typesafe/config/impl/SimpleConfigObject.java index 17d99b6d..5c6580a4 100644 --- a/config/src/main/java/com/typesafe/config/impl/SimpleConfigObject.java +++ b/config/src/main/java/com/typesafe/config/impl/SimpleConfigObject.java @@ -418,6 +418,45 @@ final class SimpleConfigObject extends AbstractConfigObject implements Serializa }); } + static final private class RenderComparator implements java.util.Comparator { + private static boolean isAllDigits(String s) { + int length = s.length(); + + // empty string doesn't count as a number + if (length == 0) + return false; + + for (int i = 0; i < length; ++i) { + char c = s.charAt(i); + + if (Character.isDigit(c)) + continue; + else + return false; + } + return true; + } + + // This is supposed to sort numbers before strings, + // and sort the numbers numerically. The point is + // to make objects which are really list-like + // (numeric indices) appear in order. + @Override + public int compare(String a, String b) { + boolean aDigits = isAllDigits(a); + boolean bDigits = isAllDigits(b); + if (aDigits && bDigits) { + return Integer.compare(Integer.parseInt(a), Integer.parseInt(b)); + } else if (aDigits) { + return -1; + } else if (bDigits) { + return 1; + } else { + return a.compareTo(b); + } + } + } + @Override protected void render(StringBuilder sb, int indent, boolean atRoot, ConfigRenderOptions options) { if (isEmpty()) { @@ -438,7 +477,7 @@ final class SimpleConfigObject extends AbstractConfigObject implements Serializa int separatorCount = 0; String[] keys = keySet().toArray(new String[size()]); - Arrays.sort(keys); + Arrays.sort(keys, new RenderComparator()); for (String k : keys) { AbstractConfigValue v; v = value.get(k); diff --git a/config/src/test/scala/com/typesafe/config/impl/ConfigValueTest.scala b/config/src/test/scala/com/typesafe/config/impl/ConfigValueTest.scala index 5a964319..0c341415 100644 --- a/config/src/test/scala/com/typesafe/config/impl/ConfigValueTest.scala +++ b/config/src/test/scala/com/typesafe/config/impl/ConfigValueTest.scala @@ -14,6 +14,7 @@ import com.typesafe.config.ConfigList import com.typesafe.config.ConfigException import com.typesafe.config.ConfigValueType import com.typesafe.config.ConfigOrigin +import com.typesafe.config.ConfigRenderOptions import com.typesafe.config.ConfigValueFactory import com.typesafe.config.ConfigFactory @@ -959,4 +960,11 @@ class ConfigValueTest extends TestUtils { assertEquals(conf, parsed) } + + @Test + def renderSorting(): Unit = { + val config = parseConfig("""0=a,1=b,2=c,3=d,10=e,20=f,30=g""") + val rendered = config.root.render(ConfigRenderOptions.concise()) + assertEquals("""{"0":"a","1":"b","2":"c","3":"d","10":"e","20":"f","30":"g"}""", rendered) + } }