David K. Bainbridge | 528b318 | 2017-01-23 08:51:59 -0800 | [diff] [blame] | 1 | package json |
| 2 | |
| 3 | import ( |
| 4 | "reflect" |
| 5 | ) |
| 6 | |
| 7 | // Extension holds a set of additional rules to be used when unmarshaling |
| 8 | // strict JSON or JSON-like content. |
| 9 | type 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 | |
| 19 | type funcExt struct { |
| 20 | key string |
| 21 | args []string |
| 22 | } |
| 23 | |
| 24 | // Extend changes the decoder behavior to consider the provided extension. |
| 25 | func (dec *Decoder) Extend(ext *Extension) { dec.d.ext = *ext } |
| 26 | |
| 27 | // Extend changes the encoder behavior to consider the provided extension. |
| 28 | func (enc *Encoder) Extend(ext *Extension) { enc.ext = *ext } |
| 29 | |
| 30 | // Extend includes in e the extensions defined in ext. |
| 31 | func (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]: ...}}. |
| 52 | func (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. |
| 61 | func (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. |
| 71 | func (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. |
| 79 | func (e *Extension) DecodeUnquotedKeys(accept bool) { |
| 80 | e.unquotedKeys = accept |
| 81 | } |
| 82 | |
| 83 | // DecodeTrailingCommas defines whether to accept trailing commas in maps and arrays. |
| 84 | func (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. |
| 90 | func (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 | } |