aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authoralandonovan <adonovan@google.com>2020-11-13 16:44:10 -0500
committerGitHub <noreply@github.com>2020-11-13 16:44:10 -0500
commite292e66a28cd2a152317d4cb315ae6f6643d38b6 (patch)
tree6e8a20b04ba0c67a34a4124bf840121d9c952f6d
parent501b6c76e7d92ccfacdee1662eaabd131a5f2b6f (diff)
downloadstarlark-go-e292e66a28cd2a152317d4cb315ae6f6643d38b6.tar.gz
starlarkjson: sort dict keys (#316)
This is for consistency with the java.starlark.net implementation and proposed spec. This is a (minor) incompatible behavior change.
-rw-r--r--starlark/testdata/json.star1
-rw-r--r--starlarkjson/json.go30
2 files changed, 15 insertions, 16 deletions
diff --git a/starlark/testdata/json.star b/starlark/testdata/json.star
index 37c9070..ef33d91 100644
--- a/starlark/testdata/json.star
+++ b/starlark/testdata/json.star
@@ -21,6 +21,7 @@ assert.eq(json.encode([1, 2, 3]), "[1,2,3]")
assert.eq(json.encode((1, 2, 3)), "[1,2,3]")
assert.eq(json.encode(range(3)), "[0,1,2]") # a built-in iterable
assert.eq(json.encode(dict(x = 1, y = "two")), '{"x":1,"y":"two"}')
+assert.eq(json.encode(dict(y = "two", x = 1)), '{"x":1,"y":"two"}') # key, not insertion, order
assert.eq(json.encode(struct(x = 1, y = "two")), '{"x":1,"y":"two"}') # a user-defined HasAttrs
assert.eq(json.encode("\x80"), '"\\ufffd"') # invalid UTF-8 -> replacement char
diff --git a/starlarkjson/json.go b/starlarkjson/json.go
index bfa397a..fc5d53f 100644
--- a/starlarkjson/json.go
+++ b/starlarkjson/json.go
@@ -137,26 +137,24 @@ func encode(thread *starlark.Thread, b *starlark.Builtin, args starlark.Tuple, k
case starlark.IterableMapping:
// e.g. dict (must have string keys)
buf.WriteByte('{')
- iter := x.Iterate()
- defer iter.Done()
- var k starlark.Value
- for i := 0; iter.Next(&k); i++ {
+ items := x.Items()
+ for _, item := range items {
+ if _, ok := item[0].(starlark.String); !ok {
+ return fmt.Errorf("%s has %s key, want string", x.Type(), item[0].Type())
+ }
+ }
+ sort.Slice(items, func(i, j int) bool {
+ return items[i][0].(starlark.String) < items[j][0].(starlark.String)
+ })
+ for i, item := range items {
if i > 0 {
buf.WriteByte(',')
}
- s, ok := starlark.AsString(k)
- if !ok {
- return fmt.Errorf("%s has %s key, want string", x.Type(), k.Type())
- }
- v, found, err := x.Get(k)
- if err != nil || !found {
- log.Fatalf("internal error: mapping %s has %s among keys but value lookup fails", x.Type(), k)
- }
-
- quote(s)
+ k, _ := starlark.AsString(item[0])
+ quote(k)
buf.WriteByte(':')
- if err := emit(v); err != nil {
- return fmt.Errorf("in %s key %s: %v", x.Type(), k, err)
+ if err := emit(item[1]); err != nil {
+ return fmt.Errorf("in %s key %s: %v", x.Type(), item[0], err)
}
}
buf.WriteByte('}')