]> Cypherpunks repositories - gostls13.git/commitdiff
encoding/json: marshal maps using reflect.Value.MapRange
authorMeng Zhuo <mzh@golangcn.org>
Thu, 21 Jan 2021 14:53:30 +0000 (22:53 +0800)
committerMeng Zhuo <mzh@golangcn.org>
Fri, 5 Mar 2021 02:28:11 +0000 (02:28 +0000)
Map serialization using reflect.Value.MapIndex cannot retrieve
map keys that contain a NaN, resulting in a panic.
Switch the implementation to use the reflect.Value.MapRange method
instead, which iterates over all map entries regardless of whether
they are directly retrievable.

Note that according to RFC 8259, section 4, a JSON object should
have unique names, but does not forbid the occurrence of duplicate names.

Fixes #43207

Change-Id: If4bc55229b1f64b8ca4b0fed37549725efdace39
Reviewed-on: https://go-review.googlesource.com/c/go/+/278632
Trust: Meng Zhuo <mzh@golangcn.org>
Trust: Joe Tsai <thebrokentoaster@gmail.com>
Trust: Bryan C. Mills <bcmills@google.com>
Run-TryBot: Meng Zhuo <mzh@golangcn.org>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Joe Tsai <thebrokentoaster@gmail.com>
Reviewed-by: Daniel Martí <mvdan@mvdan.cc>
src/encoding/json/encode.go
src/encoding/json/encode_test.go

index 751f03d33da4b053b438d7c7282e599429838e93..e473e615a9ed43f5f974853e86b220259c81c091 100644 (file)
@@ -794,23 +794,24 @@ func (me mapEncoder) encode(e *encodeState, v reflect.Value, opts encOpts) {
        e.WriteByte('{')
 
        // Extract and sort the keys.
-       keys := v.MapKeys()
-       sv := make([]reflectWithString, len(keys))
-       for i, v := range keys {
-               sv[i].v = v
+       sv := make([]reflectWithString, v.Len())
+       mi := v.MapRange()
+       for i := 0; mi.Next(); i++ {
+               sv[i].k = mi.Key()
+               sv[i].v = mi.Value()
                if err := sv[i].resolve(); err != nil {
                        e.error(fmt.Errorf("json: encoding error for type %q: %q", v.Type().String(), err.Error()))
                }
        }
-       sort.Slice(sv, func(i, j int) bool { return sv[i].s < sv[j].s })
+       sort.Slice(sv, func(i, j int) bool { return sv[i].ks < sv[j].ks })
 
        for i, kv := range sv {
                if i > 0 {
                        e.WriteByte(',')
                }
-               e.string(kv.s, opts.escapeHTML)
+               e.string(kv.ks, opts.escapeHTML)
                e.WriteByte(':')
-               me.elemEnc(e, v.MapIndex(kv.v), opts)
+               me.elemEnc(e, kv.v, opts)
        }
        e.WriteByte('}')
        e.ptrLevel--
@@ -997,29 +998,30 @@ func typeByIndex(t reflect.Type, index []int) reflect.Type {
 }
 
 type reflectWithString struct {
-       v reflect.Value
-       s string
+       k  reflect.Value
+       v  reflect.Value
+       ks string
 }
 
 func (w *reflectWithString) resolve() error {
-       if w.v.Kind() == reflect.String {
-               w.s = w.v.String()
+       if w.k.Kind() == reflect.String {
+               w.ks = w.k.String()
                return nil
        }
-       if tm, ok := w.v.Interface().(encoding.TextMarshaler); ok {
-               if w.v.Kind() == reflect.Ptr && w.v.IsNil() {
+       if tm, ok := w.k.Interface().(encoding.TextMarshaler); ok {
+               if w.k.Kind() == reflect.Ptr && w.k.IsNil() {
                        return nil
                }
                buf, err := tm.MarshalText()
-               w.s = string(buf)
+               w.ks = string(buf)
                return err
        }
-       switch w.v.Kind() {
+       switch w.k.Kind() {
        case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
-               w.s = strconv.FormatInt(w.v.Int(), 10)
+               w.ks = strconv.FormatInt(w.k.Int(), 10)
                return nil
        case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
-               w.s = strconv.FormatUint(w.v.Uint(), 10)
+               w.ks = strconv.FormatUint(w.k.Uint(), 10)
                return nil
        }
        panic("unexpected map key type")
index 42bb09d5cded0babe31a089369f325ff6b3aaf7e..0dad9510952702948d743932aeeaf583d62a0007 100644 (file)
@@ -245,6 +245,22 @@ func TestUnsupportedValues(t *testing.T) {
        }
 }
 
+// Issue 43207
+func TestMarshalTextFloatMap(t *testing.T) {
+       m := map[textfloat]string{
+               textfloat(math.NaN()): "1",
+               textfloat(math.NaN()): "1",
+       }
+       got, err := Marshal(m)
+       if err != nil {
+               t.Errorf("Marshal() error: %v", err)
+       }
+       want := `{"TF:NaN":"1","TF:NaN":"1"}`
+       if string(got) != want {
+               t.Errorf("Marshal() = %s, want %s", got, want)
+       }
+}
+
 // Ref has Marshaler and Unmarshaler methods with pointer receiver.
 type Ref int
 
@@ -854,6 +870,10 @@ func tenc(format string, a ...interface{}) ([]byte, error) {
        return buf.Bytes(), nil
 }
 
+type textfloat float64
+
+func (f textfloat) MarshalText() ([]byte, error) { return tenc(`TF:%0.2f`, f) }
+
 // Issue 13783
 func TestEncodeBytekind(t *testing.T) {
        testdata := []struct {
@@ -872,6 +892,7 @@ func TestEncodeBytekind(t *testing.T) {
                {[]jsonint{5, 4}, `[{"JI":5},{"JI":4}]`},
                {[]textint{9, 3}, `["TI:9","TI:3"]`},
                {[]int{9, 3}, `[9,3]`},
+               {[]textfloat{12, 3}, `["TF:12.00","TF:3.00"]`},
        }
        for _, d := range testdata {
                js, err := Marshal(d.data)