Skip to content

Commit ceb15f3

Browse files
fix: bugfix for setting JSON keys with special characters
1 parent eb137af commit ceb15f3

File tree

3 files changed

+15
-10
lines changed

3 files changed

+15
-10
lines changed

internal/apijson/encoder.go

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,10 @@ import (
1616

1717
var encoders sync.Map // map[encoderEntry]encoderFunc
1818

19+
// If we want to set a literal key value into JSON using sjson, we need to make sure it doesn't have
20+
// special characters that sjson interprets as a path.
21+
var EscapeSJSONKey = strings.NewReplacer("\\", "\\\\", "|", "\\|", "#", "\\#", "@", "\\@", "*", "\\*", ".", "\\.", ":", "\\:", "?", "\\?").Replace
22+
1923
func Marshal(value any) ([]byte, error) {
2024
e := &encoder{dateFormat: time.RFC3339}
2125
return e.marshal(value)
@@ -270,7 +274,7 @@ func (e *encoder) newStructTypeEncoder(t reflect.Type) encoderFunc {
270274
if encoded == nil {
271275
continue
272276
}
273-
json, err = sjson.SetRawBytes(json, ef.tag.name, encoded)
277+
json, err = sjson.SetRawBytes(json, EscapeSJSONKey(ef.tag.name), encoded)
274278
if err != nil {
275279
return nil, err
276280
}
@@ -348,7 +352,7 @@ func (e *encoder) encodeMapEntries(json []byte, v reflect.Value) ([]byte, error)
348352
}
349353
encodedKeyString = string(encodedKeyBytes)
350354
}
351-
encodedKey := []byte(sjsonReplacer.Replace(encodedKeyString))
355+
encodedKey := []byte(encodedKeyString)
352356
pairs = append(pairs, mapPair{key: encodedKey, value: iter.Value()})
353357
}
354358

@@ -366,7 +370,7 @@ func (e *encoder) encodeMapEntries(json []byte, v reflect.Value) ([]byte, error)
366370
if len(encodedValue) == 0 {
367371
continue
368372
}
369-
json, err = sjson.SetRawBytes(json, string(p.key), encodedValue)
373+
json, err = sjson.SetRawBytes(json, EscapeSJSONKey(string(p.key)), encodedValue)
370374
if err != nil {
371375
return nil, err
372376
}
@@ -386,7 +390,3 @@ func (e *encoder) newMapEncoder(_ reflect.Type) encoderFunc {
386390
return json, nil
387391
}
388392
}
389-
390-
// If we want to set a literal key value into JSON using sjson, we need to make sure it doesn't have
391-
// special characters that sjson interprets as a path.
392-
var sjsonReplacer *strings.Replacer = strings.NewReplacer(".", "\\.", ":", "\\:", "*", "\\*")

internal/apijson/union.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ func (d *decoderBuilder) newStructUnionDecoder(t reflect.Type) decoderFunc {
7878

7979
return func(n gjson.Result, v reflect.Value, state *decoderState) error {
8080
if discriminated && n.Type == gjson.JSON && len(unionEntry.discriminatorKey) != 0 {
81-
discriminator := n.Get(unionEntry.discriminatorKey).Value()
81+
discriminator := n.Get(EscapeSJSONKey(unionEntry.discriminatorKey)).Value()
8282
for _, decoder := range discriminatedDecoders {
8383
if discriminator == decoder.discriminator {
8484
inner := v.FieldByIndex(decoder.field.Index)
@@ -162,7 +162,7 @@ func (d *decoderBuilder) newUnionDecoder(t reflect.Type) decoderFunc {
162162
}
163163

164164
if len(unionEntry.discriminatorKey) != 0 {
165-
discriminatorValue := n.Get(unionEntry.discriminatorKey).Value()
165+
discriminatorValue := n.Get(EscapeSJSONKey(unionEntry.discriminatorKey)).Value()
166166
if discriminatorValue == variant.DiscriminatorValue {
167167
inner := reflect.New(variant.Type).Elem()
168168
err := decoder(n, inner, state)

packages/param/encoder.go

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import (
44
"encoding/json"
55
"fmt"
66
"reflect"
7+
"strings"
78
"time"
89

910
shimjson "github.com/llamastack/llama-stack-client-go/internal/encoding/json"
@@ -14,6 +15,10 @@ import (
1415
// EncodedAsDate is not be stable and shouldn't be relied upon
1516
type EncodedAsDate Opt[time.Time]
1617

18+
// If we want to set a literal key value into JSON using sjson, we need to make sure it doesn't have
19+
// special characters that sjson interprets as a path.
20+
var EscapeSJSONKey = strings.NewReplacer("\\", "\\\\", "|", "\\|", "#", "\\#", "@", "\\@", "*", "\\*", ".", "\\.", ":", "\\:", "?", "\\?").Replace
21+
1722
type forceOmit int
1823

1924
func (m EncodedAsDate) MarshalJSON() ([]byte, error) {
@@ -52,7 +57,7 @@ func MarshalWithExtras[T ParamStruct, R any](f T, underlying any, extras map[str
5257
}
5358
continue
5459
}
55-
bytes, err = sjson.SetBytes(bytes, k, v)
60+
bytes, err = sjson.SetBytes(bytes, EscapeSJSONKey(k), v)
5661
if err != nil {
5762
return nil, err
5863
}

0 commit comments

Comments
 (0)