diff --git a/internal/apijson/encoder.go b/internal/apijson/encoder.go index 13d8903..decc24a 100644 --- a/internal/apijson/encoder.go +++ b/internal/apijson/encoder.go @@ -7,6 +7,7 @@ import ( "reflect" "sort" "strconv" + "strings" "sync" "time" @@ -342,16 +343,18 @@ func (e *encoder) encodeMapEntries(json []byte, v reflect.Value) ([]byte, error) iter := v.MapRange() for iter.Next() { - var encodedKey []byte + var encodedKeyString string if iter.Key().Type().Kind() == reflect.String { - encodedKey = []byte(iter.Key().String()) + encodedKeyString = iter.Key().String() } else { var err error - encodedKey, err = keyEncoder(iter.Key()) + encodedKeyBytes, err := keyEncoder(iter.Key()) if err != nil { return nil, err } + encodedKeyString = string(encodedKeyBytes) } + encodedKey := []byte(sjsonReplacer.Replace(encodedKeyString)) pairs = append(pairs, mapPair{key: encodedKey, value: iter.Value()}) } @@ -389,3 +392,7 @@ func (e *encoder) newMapEncoder(t reflect.Type) encoderFunc { return json, nil } } + +// If we want to set a literal key value into JSON using sjson, we need to make sure it doesn't have +// special characters that sjson interprets as a path. +var sjsonReplacer *strings.Replacer = strings.NewReplacer(".", "\\.", ":", "\\:", "*", "\\*") diff --git a/internal/apijson/json_test.go b/internal/apijson/json_test.go index 85cd2b5..e656344 100644 --- a/internal/apijson/json_test.go +++ b/internal/apijson/json_test.go @@ -361,8 +361,9 @@ var tests = map[string]struct { "date_time_missing_timezone_colon_coerce": {`"2007-03-01T13:03:05-1200"`, time.Date(2007, time.March, 1, 13, 3, 5, 0, time.FixedZone("", -12*60*60))}, "date_time_nano_missing_t_coerce": {`"2007-03-01 13:03:05.123456789Z"`, time.Date(2007, time.March, 1, 13, 3, 5, 123456789, time.UTC)}, - "map_string": {`{"foo":"bar"}`, map[string]string{"foo": "bar"}}, - "map_interface": {`{"a":1,"b":"str","c":false}`, map[string]interface{}{"a": float64(1), "b": "str", "c": false}}, + "map_string": {`{"foo":"bar"}`, map[string]string{"foo": "bar"}}, + "map_string_with_sjson_path_chars": {`{":a.b.c*:d*-1e.f":"bar"}`, map[string]string{":a.b.c*:d*-1e.f": "bar"}}, + "map_interface": {`{"a":1,"b":"str","c":false}`, map[string]interface{}{"a": float64(1), "b": "str", "c": false}}, "primitive_struct": { `{"a":false,"b":237628372683,"c":654,"d":9999.43,"e":43.76,"f":[1,2,3,4]}`,