Skip to content

Commit 29c1796

Browse files
committed
Decode function extensions.
1 parent ab44379 commit 29c1796

File tree

4 files changed

+427
-0
lines changed

4 files changed

+427
-0
lines changed

internal/json/decode.go

Lines changed: 256 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -254,6 +254,7 @@ type decodeState struct {
254254
nextscan scanner // for calls to nextValue
255255
savedError error
256256
useNumber bool
257+
ext *Extension
257258
}
258259

259260
// errPhase is used for errors that should not happen unless
@@ -369,6 +370,9 @@ func (d *decodeState) value(v reflect.Value) {
369370

370371
case scanBeginLiteral:
371372
d.literal(v)
373+
374+
case scanBeginFunc:
375+
d.function(v)
372376
}
373377
}
374378

@@ -718,6 +722,213 @@ func (d *decodeState) object(v reflect.Value) {
718722
}
719723
}
720724

725+
// function consumes a function from d.data[d.off-1:], decoding into the value v.
726+
// the first byte of the function name has been read already.
727+
func (d *decodeState) function(v reflect.Value) {
728+
// Check for unmarshaler.
729+
u, ut, pv := d.indirect(v, false)
730+
if u != nil {
731+
d.off--
732+
err := u.UnmarshalJSON(d.next())
733+
if err != nil {
734+
d.error(err)
735+
}
736+
return
737+
}
738+
if ut != nil {
739+
d.saveError(&UnmarshalTypeError{"object", v.Type(), int64(d.off)})
740+
d.off--
741+
d.next() // skip over { } in input
742+
return
743+
}
744+
v = pv
745+
746+
// Decoding into nil interface? Switch to non-reflect code.
747+
if v.Kind() == reflect.Interface && v.NumMethod() == 0 {
748+
v.Set(reflect.ValueOf(d.functionInterface()))
749+
return
750+
}
751+
752+
nameStart := d.off - 1
753+
754+
if op := d.scanWhile(scanContinue); op != scanFuncArg {
755+
d.error(errPhase)
756+
}
757+
758+
funcName := string(d.data[nameStart : d.off-1])
759+
funcData := d.ext.funcs[funcName]
760+
if funcData.key == "" {
761+
d.error(fmt.Errorf("json: unknown function %s", funcName))
762+
}
763+
764+
// Check type of target:
765+
// struct or
766+
// map[string]T or map[encoding.TextUnmarshaler]T
767+
switch v.Kind() {
768+
case reflect.Map:
769+
// Map key must either have string kind or be an encoding.TextUnmarshaler.
770+
t := v.Type()
771+
if t.Key().Kind() != reflect.String &&
772+
!reflect.PtrTo(t.Key()).Implements(textUnmarshalerType) {
773+
d.saveError(&UnmarshalTypeError{"object", v.Type(), int64(d.off)})
774+
d.off--
775+
d.next() // skip over { } in input
776+
return
777+
}
778+
if v.IsNil() {
779+
v.Set(reflect.MakeMap(t))
780+
}
781+
case reflect.Struct:
782+
783+
default:
784+
d.saveError(&UnmarshalTypeError{"object", v.Type(), int64(d.off)})
785+
d.off--
786+
d.next() // skip over { } in input
787+
return
788+
}
789+
790+
// TODO Fix case of func field as map.
791+
//topv := v
792+
793+
// Figure out field corresponding to function.
794+
key := []byte(funcData.key)
795+
if v.Kind() == reflect.Map {
796+
elemType := v.Type().Elem()
797+
v = reflect.New(elemType).Elem()
798+
} else {
799+
var f *field
800+
fields := cachedTypeFields(v.Type())
801+
for i := range fields {
802+
ff := &fields[i]
803+
if bytes.Equal(ff.nameBytes, key) {
804+
f = ff
805+
break
806+
}
807+
if f == nil && ff.equalFold(ff.nameBytes, key) {
808+
f = ff
809+
}
810+
}
811+
if f != nil {
812+
for _, i := range f.index {
813+
if v.Kind() == reflect.Ptr {
814+
if v.IsNil() {
815+
v.Set(reflect.New(v.Type().Elem()))
816+
}
817+
v = v.Elem()
818+
}
819+
v = v.Field(i)
820+
}
821+
if v.Kind() == reflect.Ptr {
822+
if v.IsNil() {
823+
v.Set(reflect.New(v.Type().Elem()))
824+
}
825+
v = v.Elem()
826+
}
827+
}
828+
}
829+
830+
var mapElem reflect.Value
831+
832+
// Parse function arguments.
833+
for i := 0; ; i++ {
834+
// closing ) - can only happen on first iteration.
835+
op := d.scanWhile(scanSkipSpace)
836+
if op == scanEndFunc {
837+
break
838+
}
839+
840+
// Back up so d.value can have the byte we just read.
841+
d.off--
842+
d.scan.undo(op)
843+
844+
if i >= len(funcData.args) {
845+
d.error(fmt.Errorf("json: too many arguments for function %s", funcName))
846+
}
847+
key := []byte(funcData.args[i])
848+
849+
// Figure out field corresponding to key.
850+
var subv reflect.Value
851+
destring := false // whether the value is wrapped in a string to be decoded first
852+
853+
if v.Kind() == reflect.Map {
854+
elemType := v.Type().Elem()
855+
if !mapElem.IsValid() {
856+
mapElem = reflect.New(elemType).Elem()
857+
} else {
858+
mapElem.Set(reflect.Zero(elemType))
859+
}
860+
subv = mapElem
861+
} else {
862+
var f *field
863+
fields := cachedTypeFields(v.Type())
864+
for i := range fields {
865+
ff := &fields[i]
866+
if bytes.Equal(ff.nameBytes, key) {
867+
f = ff
868+
break
869+
}
870+
if f == nil && ff.equalFold(ff.nameBytes, key) {
871+
f = ff
872+
}
873+
}
874+
if f != nil {
875+
subv = v
876+
destring = f.quoted
877+
for _, i := range f.index {
878+
if subv.Kind() == reflect.Ptr {
879+
if subv.IsNil() {
880+
subv.Set(reflect.New(subv.Type().Elem()))
881+
}
882+
subv = subv.Elem()
883+
}
884+
subv = subv.Field(i)
885+
}
886+
}
887+
}
888+
889+
// Read value.
890+
if destring {
891+
switch qv := d.valueQuoted().(type) {
892+
case nil:
893+
d.literalStore(nullLiteral, subv, false)
894+
case string:
895+
d.literalStore([]byte(qv), subv, true)
896+
default:
897+
d.saveError(fmt.Errorf("json: invalid use of ,string struct tag, trying to unmarshal unquoted value into %v", subv.Type()))
898+
}
899+
} else {
900+
d.value(subv)
901+
}
902+
903+
// Write value back to map;
904+
// if using struct, subv points into struct already.
905+
if v.Kind() == reflect.Map {
906+
kt := v.Type().Key()
907+
var kv reflect.Value
908+
switch {
909+
case kt.Kind() == reflect.String:
910+
kv = reflect.ValueOf(key).Convert(v.Type().Key())
911+
case reflect.PtrTo(kt).Implements(textUnmarshalerType):
912+
kv = reflect.New(v.Type().Key())
913+
d.literalStore(key, kv, true)
914+
kv = kv.Elem()
915+
default:
916+
panic("json: Unexpected key type") // should never occur
917+
}
918+
v.SetMapIndex(kv, subv)
919+
}
920+
921+
// Next token must be , or ).
922+
op = d.scanWhile(scanSkipSpace)
923+
if op == scanEndFunc {
924+
break
925+
}
926+
if op != scanFuncArg {
927+
d.error(errPhase)
928+
}
929+
}
930+
}
931+
721932
// literal consumes a literal from d.data[d.off-1:], decoding into the value v.
722933
// The first byte of the literal has been read already
723934
// (that's how the caller knows it's a literal).
@@ -933,6 +1144,8 @@ func (d *decodeState) valueInterface() interface{} {
9331144
return d.objectInterface()
9341145
case scanBeginLiteral:
9351146
return d.literalInterface()
1147+
case scanBeginFunc:
1148+
return d.functionInterface()
9361149
}
9371150
}
9381151

@@ -1047,6 +1260,49 @@ func (d *decodeState) literalInterface() interface{} {
10471260
}
10481261
}
10491262

1263+
// functionInterface is like function but returns map[string]interface{}.
1264+
func (d *decodeState) functionInterface() map[string]interface{} {
1265+
nameStart := d.off - 1
1266+
1267+
if op := d.scanWhile(scanContinue); op != scanFuncArg {
1268+
d.error(errPhase)
1269+
}
1270+
1271+
funcName := string(d.data[nameStart : d.off-1])
1272+
funcData := d.ext.funcs[funcName]
1273+
if funcData.key == "" {
1274+
d.error(fmt.Errorf("json: unknown function %s", funcName))
1275+
}
1276+
1277+
m := make(map[string]interface{})
1278+
for i := 0; ; i++ {
1279+
// Look ahead for ) - can only happen on first iteration.
1280+
op := d.scanWhile(scanSkipSpace)
1281+
if op == scanEndFunc {
1282+
break
1283+
}
1284+
1285+
// Back up so d.value can have the byte we just read.
1286+
d.off--
1287+
d.scan.undo(op)
1288+
1289+
if i >= len(funcData.args) {
1290+
d.error(fmt.Errorf("json: too many arguments for function %s", funcName))
1291+
}
1292+
m[funcData.args[i]] = d.valueInterface()
1293+
1294+
// Next token must be , or ).
1295+
op = d.scanWhile(scanSkipSpace)
1296+
if op == scanEndFunc {
1297+
break
1298+
}
1299+
if op != scanFuncArg {
1300+
d.error(errPhase)
1301+
}
1302+
}
1303+
return map[string]interface{}{funcData.key: m}
1304+
}
1305+
10501306
// getu4 decodes \uXXXX from the beginning of s, returning the hex value,
10511307
// or it returns -1.
10521308
func getu4(s []byte) rune {

internal/json/extension.go

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
package json
2+
3+
// Extension holds a set of additional rules to be used when unmarshaling
4+
// strict JSON or JSON-like content.
5+
type Extension struct {
6+
funcs map[string]funcExt
7+
keyed map[string]func() interface{}
8+
}
9+
10+
type funcExt struct {
11+
key string
12+
args []string
13+
}
14+
15+
// Extend changes the decoder behavior to consider the provided extension.
16+
func (dec *Decoder) Extend(ext *Extension) { dec.d.ext = ext }
17+
18+
// Func defines a function call that may be observed inside JSON content.
19+
// A function with the provided name will be unmarshaled as the document
20+
// {key: {args[0]: ..., args[N]: ...}}.
21+
func (e *Extension) Func(name string, key string, args ...string) {
22+
if e.funcs == nil {
23+
e.funcs = make(map[string]funcExt)
24+
}
25+
e.funcs[name] = funcExt{key, args}
26+
}
27+
28+
// KeyedDoc defines a key that when observed as the first element inside a
29+
// JSON document or sub-document triggers the parsing of that document as
30+
// the value returned by the provided function.
31+
func (e *Extension) KeyedDoc(key string, new func() interface{}) {
32+
if e.keyed == nil {
33+
e.keyed = make(map[string]func() interface{})
34+
}
35+
e.keyed[key] = new
36+
}

0 commit comments

Comments
 (0)