blob: 1c8fd459753f6f388b75f753ab6cf4ef46b4f4f1 [file] [log] [blame]
David K. Bainbridge528b3182017-01-23 08:51:59 -08001package json
2
3import (
4 "reflect"
5)
6
7// Extension holds a set of additional rules to be used when unmarshaling
8// strict JSON or JSON-like content.
9type Extension struct {
10 funcs map[string]funcExt
11 consts map[string]interface{}
12 keyed map[string]func([]byte) (interface{}, error)
13 encode map[reflect.Type]func(v interface{}) ([]byte, error)
14
15 unquotedKeys bool
16 trailingCommas bool
17}
18
19type funcExt struct {
20 key string
21 args []string
22}
23
24// Extend changes the decoder behavior to consider the provided extension.
25func (dec *Decoder) Extend(ext *Extension) { dec.d.ext = *ext }
26
27// Extend changes the encoder behavior to consider the provided extension.
28func (enc *Encoder) Extend(ext *Extension) { enc.ext = *ext }
29
30// Extend includes in e the extensions defined in ext.
31func (e *Extension) Extend(ext *Extension) {
32 for name, fext := range ext.funcs {
33 e.DecodeFunc(name, fext.key, fext.args...)
34 }
35 for name, value := range ext.consts {
36 e.DecodeConst(name, value)
37 }
38 for key, decode := range ext.keyed {
39 e.DecodeKeyed(key, decode)
40 }
41 for typ, encode := range ext.encode {
42 if e.encode == nil {
43 e.encode = make(map[reflect.Type]func(v interface{}) ([]byte, error))
44 }
45 e.encode[typ] = encode
46 }
47}
48
49// DecodeFunc defines a function call that may be observed inside JSON content.
50// A function with the provided name will be unmarshaled as the document
51// {key: {args[0]: ..., args[N]: ...}}.
52func (e *Extension) DecodeFunc(name string, key string, args ...string) {
53 if e.funcs == nil {
54 e.funcs = make(map[string]funcExt)
55 }
56 e.funcs[name] = funcExt{key, args}
57}
58
59// DecodeConst defines a constant name that may be observed inside JSON content
60// and will be decoded with the provided value.
61func (e *Extension) DecodeConst(name string, value interface{}) {
62 if e.consts == nil {
63 e.consts = make(map[string]interface{})
64 }
65 e.consts[name] = value
66}
67
68// DecodeKeyed defines a key that when observed as the first element inside a
69// JSON document triggers the decoding of that document via the provided
70// decode function.
71func (e *Extension) DecodeKeyed(key string, decode func(data []byte) (interface{}, error)) {
72 if e.keyed == nil {
73 e.keyed = make(map[string]func([]byte) (interface{}, error))
74 }
75 e.keyed[key] = decode
76}
77
78// DecodeUnquotedKeys defines whether to accept map keys that are unquoted strings.
79func (e *Extension) DecodeUnquotedKeys(accept bool) {
80 e.unquotedKeys = accept
81}
82
83// DecodeTrailingCommas defines whether to accept trailing commas in maps and arrays.
84func (e *Extension) DecodeTrailingCommas(accept bool) {
85 e.trailingCommas = accept
86}
87
88// EncodeType registers a function to encode values with the same type of the
89// provided sample.
90func (e *Extension) EncodeType(sample interface{}, encode func(v interface{}) ([]byte, error)) {
91 if e.encode == nil {
92 e.encode = make(map[reflect.Type]func(v interface{}) ([]byte, error))
93 }
94 e.encode[reflect.TypeOf(sample)] = encode
95}