Skip to content

Commit a7526bb

Browse files
committed
encoding/json: marshal maps using reflect.Value.MapRange
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 <[email protected]> Trust: Joe Tsai <[email protected]> Trust: Bryan C. Mills <[email protected]> Run-TryBot: Meng Zhuo <[email protected]> TryBot-Result: Go Bot <[email protected]> Reviewed-by: Joe Tsai <[email protected]> Reviewed-by: Daniel Martí <[email protected]>
1 parent 96a96a9 commit a7526bb

File tree

2 files changed

+40
-17
lines changed

2 files changed

+40
-17
lines changed

src/encoding/json/encode.go

Lines changed: 19 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -794,23 +794,24 @@ func (me mapEncoder) encode(e *encodeState, v reflect.Value, opts encOpts) {
794794
e.WriteByte('{')
795795

796796
// Extract and sort the keys.
797-
keys := v.MapKeys()
798-
sv := make([]reflectWithString, len(keys))
799-
for i, v := range keys {
800-
sv[i].v = v
797+
sv := make([]reflectWithString, v.Len())
798+
mi := v.MapRange()
799+
for i := 0; mi.Next(); i++ {
800+
sv[i].k = mi.Key()
801+
sv[i].v = mi.Value()
801802
if err := sv[i].resolve(); err != nil {
802803
e.error(fmt.Errorf("json: encoding error for type %q: %q", v.Type().String(), err.Error()))
803804
}
804805
}
805-
sort.Slice(sv, func(i, j int) bool { return sv[i].s < sv[j].s })
806+
sort.Slice(sv, func(i, j int) bool { return sv[i].ks < sv[j].ks })
806807

807808
for i, kv := range sv {
808809
if i > 0 {
809810
e.WriteByte(',')
810811
}
811-
e.string(kv.s, opts.escapeHTML)
812+
e.string(kv.ks, opts.escapeHTML)
812813
e.WriteByte(':')
813-
me.elemEnc(e, v.MapIndex(kv.v), opts)
814+
me.elemEnc(e, kv.v, opts)
814815
}
815816
e.WriteByte('}')
816817
e.ptrLevel--
@@ -997,29 +998,30 @@ func typeByIndex(t reflect.Type, index []int) reflect.Type {
997998
}
998999

9991000
type reflectWithString struct {
1000-
v reflect.Value
1001-
s string
1001+
k reflect.Value
1002+
v reflect.Value
1003+
ks string
10021004
}
10031005

10041006
func (w *reflectWithString) resolve() error {
1005-
if w.v.Kind() == reflect.String {
1006-
w.s = w.v.String()
1007+
if w.k.Kind() == reflect.String {
1008+
w.ks = w.k.String()
10071009
return nil
10081010
}
1009-
if tm, ok := w.v.Interface().(encoding.TextMarshaler); ok {
1010-
if w.v.Kind() == reflect.Ptr && w.v.IsNil() {
1011+
if tm, ok := w.k.Interface().(encoding.TextMarshaler); ok {
1012+
if w.k.Kind() == reflect.Ptr && w.k.IsNil() {
10111013
return nil
10121014
}
10131015
buf, err := tm.MarshalText()
1014-
w.s = string(buf)
1016+
w.ks = string(buf)
10151017
return err
10161018
}
1017-
switch w.v.Kind() {
1019+
switch w.k.Kind() {
10181020
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
1019-
w.s = strconv.FormatInt(w.v.Int(), 10)
1021+
w.ks = strconv.FormatInt(w.k.Int(), 10)
10201022
return nil
10211023
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
1022-
w.s = strconv.FormatUint(w.v.Uint(), 10)
1024+
w.ks = strconv.FormatUint(w.k.Uint(), 10)
10231025
return nil
10241026
}
10251027
panic("unexpected map key type")

src/encoding/json/encode_test.go

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -245,6 +245,22 @@ func TestUnsupportedValues(t *testing.T) {
245245
}
246246
}
247247

248+
// Issue 43207
249+
func TestMarshalTextFloatMap(t *testing.T) {
250+
m := map[textfloat]string{
251+
textfloat(math.NaN()): "1",
252+
textfloat(math.NaN()): "1",
253+
}
254+
got, err := Marshal(m)
255+
if err != nil {
256+
t.Errorf("Marshal() error: %v", err)
257+
}
258+
want := `{"TF:NaN":"1","TF:NaN":"1"}`
259+
if string(got) != want {
260+
t.Errorf("Marshal() = %s, want %s", got, want)
261+
}
262+
}
263+
248264
// Ref has Marshaler and Unmarshaler methods with pointer receiver.
249265
type Ref int
250266

@@ -854,6 +870,10 @@ func tenc(format string, a ...interface{}) ([]byte, error) {
854870
return buf.Bytes(), nil
855871
}
856872

873+
type textfloat float64
874+
875+
func (f textfloat) MarshalText() ([]byte, error) { return tenc(`TF:%0.2f`, f) }
876+
857877
// Issue 13783
858878
func TestEncodeBytekind(t *testing.T) {
859879
testdata := []struct {
@@ -872,6 +892,7 @@ func TestEncodeBytekind(t *testing.T) {
872892
{[]jsonint{5, 4}, `[{"JI":5},{"JI":4}]`},
873893
{[]textint{9, 3}, `["TI:9","TI:3"]`},
874894
{[]int{9, 3}, `[9,3]`},
895+
{[]textfloat{12, 3}, `["TF:12.00","TF:3.00"]`},
875896
}
876897
for _, d := range testdata {
877898
js, err := Marshal(d.data)

0 commit comments

Comments
 (0)