diff options
author | alandonovan <adonovan@google.com> | 2020-11-13 16:44:10 -0500 |
---|---|---|
committer | GitHub <noreply@github.com> | 2020-11-13 16:44:10 -0500 |
commit | e292e66a28cd2a152317d4cb315ae6f6643d38b6 (patch) | |
tree | 6e8a20b04ba0c67a34a4124bf840121d9c952f6d | |
parent | 501b6c76e7d92ccfacdee1662eaabd131a5f2b6f (diff) | |
download | starlark-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.star | 1 | ||||
-rw-r--r-- | starlarkjson/json.go | 30 |
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('}') |