package json import ( "reflect" ) // Extension holds a set of additional rules to be used when unmarshaling // strict JSON or JSON-like content. type Extension struct { funcs map[string]funcExt consts map[string]interface{} keyed map[string]func([]byte) (interface{}, error) encode map[reflect.Type]func(v interface{}) ([]byte, error) unquotedKeys bool trailingCommas bool } type funcExt struct { key string args []string } // Extend changes the decoder behavior to consider the provided extension. func (dec *Decoder) Extend(ext *Extension) { dec.d.ext = *ext } // Extend changes the encoder behavior to consider the provided extension. func (enc *Encoder) Extend(ext *Extension) { enc.ext = *ext } // Extend includes in e the extensions defined in ext. func (e *Extension) Extend(ext *Extension) { for name, fext := range ext.funcs { e.DecodeFunc(name, fext.key, fext.args...) } for name, value := range ext.consts { e.DecodeConst(name, value) } for key, decode := range ext.keyed { e.DecodeKeyed(key, decode) } for typ, encode := range ext.encode { if e.encode == nil { e.encode = make(map[reflect.Type]func(v interface{}) ([]byte, error)) } e.encode[typ] = encode } } // DecodeFunc defines a function call that may be observed inside JSON content. // A function with the provided name will be unmarshaled as the document // {key: {args[0]: ..., args[N]: ...}}. func (e *Extension) DecodeFunc(name string, key string, args ...string) { if e.funcs == nil { e.funcs = make(map[string]funcExt) } e.funcs[name] = funcExt{key, args} } // DecodeConst defines a constant name that may be observed inside JSON content // and will be decoded with the provided value. func (e *Extension) DecodeConst(name string, value interface{}) { if e.consts == nil { e.consts = make(map[string]interface{}) } e.consts[name] = value } // DecodeKeyed defines a key that when observed as the first element inside a // JSON document triggers the decoding of that document via the provided // decode function. func (e *Extension) DecodeKeyed(key string, decode func(data []byte) (interface{}, error)) { if e.keyed == nil { e.keyed = make(map[string]func([]byte) (interface{}, error)) } e.keyed[key] = decode } // DecodeUnquotedKeys defines whether to accept map keys that are unquoted strings. func (e *Extension) DecodeUnquotedKeys(accept bool) { e.unquotedKeys = accept } // DecodeTrailingCommas defines whether to accept trailing commas in maps and arrays. func (e *Extension) DecodeTrailingCommas(accept bool) { e.trailingCommas = accept } // EncodeType registers a function to encode values with the same type of the // provided sample. func (e *Extension) EncodeType(sample interface{}, encode func(v interface{}) ([]byte, error)) { if e.encode == nil { e.encode = make(map[reflect.Type]func(v interface{}) ([]byte, error)) } e.encode[reflect.TypeOf(sample)] = encode }