1
0
mirror of https://github.com/json-iterator/go.git synced 2025-02-19 19:59:49 +02:00

#66 Make extension api like the java version

This commit is contained in:
Tao Wen 2017-06-20 10:41:54 +08:00
parent 499412ec4c
commit be221df432
4 changed files with 229 additions and 220 deletions

View File

@ -12,7 +12,6 @@ import (
type Config struct {
IndentionStep int
MarshalFloatWith6Digits bool
SupportUnexportedStructFields bool
EscapeHtml bool
SortMapKeys bool
UseNumber bool
@ -24,7 +23,7 @@ type frozenConfig struct {
indentionStep int
decoderCache unsafe.Pointer
encoderCache unsafe.Pointer
extensions []ExtensionFunc
extensions []Extension
streamPool chan *Stream
iteratorPool chan *Iterator
}
@ -65,9 +64,6 @@ func (cfg Config) Froze() *frozenConfig {
if cfg.MarshalFloatWith6Digits {
frozenConfig.marshalFloatWith6Digits()
}
if cfg.SupportUnexportedStructFields {
frozenConfig.supportUnexportedStructFields()
}
if cfg.EscapeHtml {
frozenConfig.escapeHtml()
}
@ -88,17 +84,10 @@ func (cfg *frozenConfig) useNumber() {
}})
}
// RegisterExtension can register a custom extension
func (cfg *frozenConfig) registerExtension(extension ExtensionFunc) {
func (cfg *frozenConfig) registerExtension(extension Extension) {
cfg.extensions = append(cfg.extensions, extension)
}
func (cfg *frozenConfig) supportUnexportedStructFields() {
cfg.registerExtension(func(type_ reflect.Type, field *reflect.StructField) ([]string, EncoderFunc, DecoderFunc) {
return []string{field.Name}, nil, nil
})
}
type lossyFloat32Encoder struct {
}

View File

@ -4,28 +4,54 @@ import (
"reflect"
"fmt"
"unsafe"
"strings"
"unicode"
)
var typeDecoders map[string]ValDecoder
var fieldDecoders map[string]ValDecoder
var typeEncoders map[string]ValEncoder
var fieldEncoders map[string]ValEncoder
var extensions []ExtensionFunc
var typeDecoders = map[string]ValDecoder{}
var fieldDecoders = map[string]ValDecoder{}
var typeEncoders = map[string]ValEncoder{}
var fieldEncoders = map[string]ValEncoder{}
var extensions = []Extension{}
type ExtensionFunc func(typ reflect.Type, field *reflect.StructField) ([]string, EncoderFunc, DecoderFunc)
type StructDescriptor struct {
Type reflect.Type
Fields map[string]*Binding
}
type Binding struct {
Field *reflect.StructField
FromNames []string
ToNames []string
ShouldOmitEmpty bool
Encoder ValEncoder
Decoder ValDecoder
}
type Extension interface {
UpdateStructDescriptor(structDescriptor *StructDescriptor)
CreateDecoder(typ reflect.Type) ValDecoder
CreateEncoder(typ reflect.Type) ValEncoder
}
type DummyExtension struct {
}
func (extension *DummyExtension) UpdateStructDescriptor(structDescriptor *StructDescriptor) {
}
func (extension *DummyExtension) CreateDecoder(typ reflect.Type) ValDecoder {
return nil
}
func (extension *DummyExtension) CreateEncoder(typ reflect.Type) ValEncoder {
return nil
}
type funcDecoder struct {
fun DecoderFunc
}
func init() {
typeDecoders = map[string]ValDecoder{}
fieldDecoders = map[string]ValDecoder{}
typeEncoders = map[string]ValEncoder{}
fieldEncoders = map[string]ValEncoder{}
extensions = []ExtensionFunc{}
}
func RegisterTypeDecoderFunc(typ string, fun DecoderFunc) {
typeDecoders[typ] = &funcDecoder{fun}
}
@ -58,36 +84,134 @@ func RegisterFieldEncoder(typ string, field string, encoder ValEncoder) {
fieldEncoders[fmt.Sprintf("%s/%s", typ, field)] = encoder
}
func RegisterExtension(extension ExtensionFunc) {
func RegisterExtension(extension Extension) {
extensions = append(extensions, extension)
}
func getTypeDecoderFromExtension(typ reflect.Type) ValDecoder {
for _, extension := range extensions {
decoder := extension.CreateDecoder(typ)
if decoder != nil {
return decoder
}
}
typeName := typ.String()
typeDecoder := typeDecoders[typeName]
if typeDecoder != nil {
return typeDecoder
decoder := typeDecoders[typeName]
if decoder != nil {
return decoder
}
if typ.Kind() == reflect.Ptr {
typeDecoder := typeDecoders[typ.Elem().String()]
if typeDecoder != nil {
return &optionalDecoder{typ.Elem(), typeDecoder}
decoder := typeDecoders[typ.Elem().String()]
if decoder != nil {
return &optionalDecoder{typ.Elem(), decoder}
}
}
return nil
}
func getTypeEncoderFromExtension(typ reflect.Type) ValEncoder {
for _, extension := range extensions {
encoder := extension.CreateEncoder(typ)
if encoder != nil {
return encoder
}
}
typeName := typ.String()
typeEncoder := typeEncoders[typeName]
if typeEncoder != nil {
return typeEncoder
encoder := typeEncoders[typeName]
if encoder != nil {
return encoder
}
if typ.Kind() == reflect.Ptr {
typeEncoder := typeEncoders[typ.Elem().String()]
if typeEncoder != nil {
return &optionalEncoder{typeEncoder}
encoder := typeEncoders[typ.Elem().String()]
if encoder != nil {
return &optionalEncoder{encoder}
}
}
return nil
}
func describeStruct(cfg *frozenConfig, typ reflect.Type) (*StructDescriptor, error) {
bindings := map[string]*Binding{}
for _, field := range listStructFields(typ) {
tagParts := strings.Split(field.Tag.Get("json"), ",")
fieldNames := calcFieldNames(field.Name, tagParts[0])
fieldCacheKey := fmt.Sprintf("%s/%s", typ.String(), field.Name)
decoder := fieldDecoders[fieldCacheKey]
if decoder == nil && len(fieldNames) > 0 {
var err error
decoder, err = decoderOfType(cfg, field.Type)
if err != nil {
return nil, err
}
}
encoder := fieldEncoders[fieldCacheKey]
if encoder == nil && len(fieldNames) > 0 {
var err error
encoder, err = encoderOfType(cfg, field.Type)
if err != nil {
return nil, err
}
// map is stored as pointer in the struct
if field.Type.Kind() == reflect.Map {
encoder = &optionalEncoder{encoder}
}
}
binding := &Binding{
Field: field,
FromNames: fieldNames,
ToNames: fieldNames,
Decoder: decoder,
Encoder: encoder,
}
for _, tagPart := range tagParts[1:] {
if tagPart == "omitempty" {
binding.ShouldOmitEmpty = true
} else if tagPart == "string" {
binding.Decoder = &stringModeDecoder{binding.Decoder}
binding.Encoder = &stringModeEncoder{binding.Encoder}
}
}
bindings[field.Name] = binding
}
structDescriptor := &StructDescriptor{
Type: typ,
Fields: bindings,
}
for _, extension := range extensions {
extension.UpdateStructDescriptor(structDescriptor)
}
return structDescriptor, nil
}
func listStructFields(typ reflect.Type) []*reflect.StructField {
fields := []*reflect.StructField{}
for i := 0; i < typ.NumField(); i++ {
field := typ.Field(i)
if field.Anonymous {
fields = append(fields, listStructFields(field.Type)...)
} else {
fields = append(fields, &field)
}
}
return fields
}
func calcFieldNames(originalFieldName string, tagProvidedFieldName string) []string {
// tag => exported? => original
isNotExported := unicode.IsLower(rune(originalFieldName[0]))
var fieldNames []string
/// tagParts[0] always present, even if no tags
switch tagProvidedFieldName {
case "":
if isNotExported {
fieldNames = []string{}
} else {
fieldNames = []string{originalFieldName}
}
case "-":
fieldNames = []string{}
default:
fieldNames = []string{tagProvidedFieldName}
}
return fieldNames
}

View File

@ -4,64 +4,19 @@ import (
"fmt"
"io"
"reflect"
"strings"
"unicode"
"unsafe"
)
func encoderOfStruct(cfg *frozenConfig, typ reflect.Type) (ValEncoder, error) {
structEncoder_ := &structEncoder{}
fields := map[string]*structFieldEncoder{}
for _, field := range listStructFields(typ) {
fieldEncoderKey := fmt.Sprintf("%s/%s", typ.String(), field.Name)
var extensionProvidedFieldNames []string
for _, extension := range extensions {
alternativeFieldNames, fun, _ := extension(typ, field)
if alternativeFieldNames != nil {
extensionProvidedFieldNames = alternativeFieldNames
}
if fun != nil {
fieldEncoders[fieldEncoderKey] = &funcEncoder{fun, nil}
}
}
for _, extension := range cfg.extensions {
alternativeFieldNames, fun, _ := extension(typ, field)
if alternativeFieldNames != nil {
extensionProvidedFieldNames = alternativeFieldNames
}
if fun != nil {
fieldEncoders[fieldEncoderKey] = &funcEncoder{fun, nil}
}
}
tagParts := strings.Split(field.Tag.Get("json"), ",")
// if fieldNames set by extension, use theirs, otherwise try tags
fieldNames := calcFieldNames(field.Name, tagParts[0], extensionProvidedFieldNames)
omitempty := false
stringMode := false
for _, tagPart := range tagParts[1:] {
if tagPart == "omitempty" {
omitempty = true
} else if tagPart == "string" {
stringMode = true
}
}
encoder := fieldEncoders[fieldEncoderKey]
var err error
if encoder == nil && len(fieldNames) > 0 {
encoder, err = encoderOfType(cfg, field.Type)
if err != nil {
return prefix(fmt.Sprintf("{%s}", field.Name)).addToEncoder(encoder, err)
}
// map is stored as pointer in the struct
if field.Type.Kind() == reflect.Map {
encoder = &optionalEncoder{encoder}
}
}
if stringMode {
encoder = &stringModeEncoder{encoder}
}
for _, fieldName := range fieldNames {
fields[fieldName] = &structFieldEncoder{field, fieldName, encoder, omitempty}
structDescriptor, err := describeStruct(cfg, typ)
if err != nil {
return nil, err
}
for _, binding := range structDescriptor.Fields {
for _, fieldName := range binding.ToNames {
fields[fieldName] = &structFieldEncoder{binding.Field, fieldName, binding.Encoder, binding.ShouldOmitEmpty}
}
}
if len(fields) == 0 {
@ -73,88 +28,20 @@ func encoderOfStruct(cfg *frozenConfig, typ reflect.Type) (ValEncoder, error) {
return structEncoder_, nil
}
func listStructFields(typ reflect.Type) []*reflect.StructField {
fields := []*reflect.StructField{}
for i := 0; i < typ.NumField(); i++ {
field := typ.Field(i)
if field.Anonymous {
fields = append(fields, listStructFields(field.Type)...)
} else {
fields = append(fields, &field)
}
}
return fields
}
func decoderOfStruct(cfg *frozenConfig, typ reflect.Type) (ValDecoder, error) {
fields := map[string]*structFieldDecoder{}
for _, field := range listStructFields(typ) {
fieldDecoderKey := fmt.Sprintf("%s/%s", typ.String(), field.Name)
var extensionProviedFieldNames []string
for _, extension := range extensions {
alternativeFieldNames, _, fun := extension(typ, field)
if alternativeFieldNames != nil {
extensionProviedFieldNames = alternativeFieldNames
}
if fun != nil {
fieldDecoders[fieldDecoderKey] = &funcDecoder{fun}
}
}
for _, extension := range cfg.extensions {
alternativeFieldNames, _, fun := extension(typ, field)
if alternativeFieldNames != nil {
extensionProviedFieldNames = alternativeFieldNames
}
if fun != nil {
fieldDecoders[fieldDecoderKey] = &funcDecoder{fun}
}
}
decoder := fieldDecoders[fieldDecoderKey]
tagParts := strings.Split(field.Tag.Get("json"), ",")
fieldNames := calcFieldNames(field.Name, tagParts[0], extensionProviedFieldNames)
if decoder == nil && len(fieldNames) > 0 {
var err error
decoder, err = decoderOfType(cfg, field.Type)
if err != nil {
return prefix(fmt.Sprintf("{%s}", field.Name)).addToDecoder(decoder, err)
}
}
for _, tagPart := range tagParts[1:] {
if tagPart == "string" {
decoder = &stringModeDecoder{decoder}
}
}
for _, fieldName := range fieldNames {
fields[fieldName] = &structFieldDecoder{field, decoder}
structDescriptor, err := describeStruct(cfg, typ)
if err != nil {
return nil, err
}
for _, binding := range structDescriptor.Fields {
for _, fieldName := range binding.FromNames {
fields[fieldName] = &structFieldDecoder{binding.Field, binding.Decoder}
}
}
return createStructDecoder(typ, fields)
}
func calcFieldNames(originalFieldName string, tagProvidedFieldName string, extensionProvidedFieldNames []string) []string {
// tag => extension => exported? => original
isNotExported := unicode.IsLower(rune(originalFieldName[0]))
var fieldNames []string
/// tagParts[0] always present, even if no tags
switch tagProvidedFieldName {
case "":
if extensionProvidedFieldNames != nil {
fieldNames = extensionProvidedFieldNames
} else {
if isNotExported {
fieldNames = []string{}
} else {
fieldNames = []string{originalFieldName}
}
}
case "-":
fieldNames = []string{}
default:
fieldNames = []string{tagProvidedFieldName}
}
return fieldNames
}
func createStructDecoder(typ reflect.Type, fields map[string]*structFieldDecoder) (ValDecoder, error) {
knownHash := map[int32]struct{}{
0: {},
@ -222,7 +109,7 @@ func createStructDecoder(typ reflect.Type, fields map[string]*structFieldDecoder
}
}
return &threeFieldsStructDecoder{typ,
fieldName1, fieldDecoder1, fieldName2, fieldDecoder2, fieldName3, fieldDecoder3}, nil
fieldName1, fieldDecoder1, fieldName2, fieldDecoder2, fieldName3, fieldDecoder3}, nil
case 4:
var fieldName1 int32
var fieldName2 int32
@ -255,8 +142,8 @@ func createStructDecoder(typ reflect.Type, fields map[string]*structFieldDecoder
}
}
return &fourFieldsStructDecoder{typ,
fieldName1, fieldDecoder1, fieldName2, fieldDecoder2, fieldName3, fieldDecoder3,
fieldName4, fieldDecoder4}, nil
fieldName1, fieldDecoder1, fieldName2, fieldDecoder2, fieldName3, fieldDecoder3,
fieldName4, fieldDecoder4}, nil
case 5:
var fieldName1 int32
var fieldName2 int32
@ -294,8 +181,8 @@ func createStructDecoder(typ reflect.Type, fields map[string]*structFieldDecoder
}
}
return &fiveFieldsStructDecoder{typ,
fieldName1, fieldDecoder1, fieldName2, fieldDecoder2, fieldName3, fieldDecoder3,
fieldName4, fieldDecoder4, fieldName5, fieldDecoder5}, nil
fieldName1, fieldDecoder1, fieldName2, fieldDecoder2, fieldName3, fieldDecoder3,
fieldName4, fieldDecoder4, fieldName5, fieldDecoder5}, nil
case 6:
var fieldName1 int32
var fieldName2 int32
@ -338,8 +225,8 @@ func createStructDecoder(typ reflect.Type, fields map[string]*structFieldDecoder
}
}
return &sixFieldsStructDecoder{typ,
fieldName1, fieldDecoder1, fieldName2, fieldDecoder2, fieldName3, fieldDecoder3,
fieldName4, fieldDecoder4, fieldName5, fieldDecoder5, fieldName6, fieldDecoder6}, nil
fieldName1, fieldDecoder1, fieldName2, fieldDecoder2, fieldName3, fieldDecoder3,
fieldName4, fieldDecoder4, fieldName5, fieldDecoder5, fieldName6, fieldDecoder6}, nil
case 7:
var fieldName1 int32
var fieldName2 int32
@ -387,9 +274,9 @@ func createStructDecoder(typ reflect.Type, fields map[string]*structFieldDecoder
}
}
return &sevenFieldsStructDecoder{typ,
fieldName1, fieldDecoder1, fieldName2, fieldDecoder2, fieldName3, fieldDecoder3,
fieldName4, fieldDecoder4, fieldName5, fieldDecoder5, fieldName6, fieldDecoder6,
fieldName7, fieldDecoder7}, nil
fieldName1, fieldDecoder1, fieldName2, fieldDecoder2, fieldName3, fieldDecoder3,
fieldName4, fieldDecoder4, fieldName5, fieldDecoder5, fieldName6, fieldDecoder6,
fieldName7, fieldDecoder7}, nil
case 8:
var fieldName1 int32
var fieldName2 int32
@ -442,9 +329,9 @@ func createStructDecoder(typ reflect.Type, fields map[string]*structFieldDecoder
}
}
return &eightFieldsStructDecoder{typ,
fieldName1, fieldDecoder1, fieldName2, fieldDecoder2, fieldName3, fieldDecoder3,
fieldName4, fieldDecoder4, fieldName5, fieldDecoder5, fieldName6, fieldDecoder6,
fieldName7, fieldDecoder7, fieldName8, fieldDecoder8}, nil
fieldName1, fieldDecoder1, fieldName2, fieldDecoder2, fieldName3, fieldDecoder3,
fieldName4, fieldDecoder4, fieldName5, fieldDecoder5, fieldName6, fieldDecoder6,
fieldName7, fieldDecoder7, fieldName8, fieldDecoder8}, nil
case 9:
var fieldName1 int32
var fieldName2 int32
@ -502,9 +389,9 @@ func createStructDecoder(typ reflect.Type, fields map[string]*structFieldDecoder
}
}
return &nineFieldsStructDecoder{typ,
fieldName1, fieldDecoder1, fieldName2, fieldDecoder2, fieldName3, fieldDecoder3,
fieldName4, fieldDecoder4, fieldName5, fieldDecoder5, fieldName6, fieldDecoder6,
fieldName7, fieldDecoder7, fieldName8, fieldDecoder8, fieldName9, fieldDecoder9}, nil
fieldName1, fieldDecoder1, fieldName2, fieldDecoder2, fieldName3, fieldDecoder3,
fieldName4, fieldDecoder4, fieldName5, fieldDecoder5, fieldName6, fieldDecoder6,
fieldName7, fieldDecoder7, fieldName8, fieldDecoder8, fieldName9, fieldDecoder9}, nil
case 10:
var fieldName1 int32
var fieldName2 int32
@ -567,10 +454,10 @@ func createStructDecoder(typ reflect.Type, fields map[string]*structFieldDecoder
}
}
return &tenFieldsStructDecoder{typ,
fieldName1, fieldDecoder1, fieldName2, fieldDecoder2, fieldName3, fieldDecoder3,
fieldName4, fieldDecoder4, fieldName5, fieldDecoder5, fieldName6, fieldDecoder6,
fieldName7, fieldDecoder7, fieldName8, fieldDecoder8, fieldName9, fieldDecoder9,
fieldName10, fieldDecoder10}, nil
fieldName1, fieldDecoder1, fieldName2, fieldDecoder2, fieldName3, fieldDecoder3,
fieldName4, fieldDecoder4, fieldName5, fieldDecoder5, fieldName6, fieldDecoder6,
fieldName7, fieldDecoder7, fieldName8, fieldDecoder8, fieldName9, fieldDecoder9,
fieldName10, fieldDecoder10}, nil
}
return &generalStructDecoder{typ, fields}, nil
}

View File

@ -3,7 +3,6 @@ package jsoniter
import (
"encoding/json"
"github.com/json-iterator/go/require"
"reflect"
"strconv"
"testing"
"time"
@ -86,22 +85,30 @@ type TestObject1 struct {
field1 string
}
type testExtension struct {
DummyExtension
}
func (extension *testExtension) UpdateStructDescriptor(structDescriptor *StructDescriptor) {
if structDescriptor.Type.String() != "jsoniter.TestObject1" {
return
}
binding := structDescriptor.Fields["field1"]
binding.Encoder = &funcEncoder{fun: func(ptr unsafe.Pointer, stream *Stream) {
str := *((*string)(ptr))
val, _ := strconv.Atoi(str)
stream.WriteInt(val)
}}
binding.Decoder = &funcDecoder{func(ptr unsafe.Pointer, iter *Iterator) {
*((*string)(ptr)) = strconv.Itoa(iter.ReadInt())
}}
binding.ToNames = []string{"field-1"}
binding.FromNames = []string{"field-1"}
}
func Test_customize_field_by_extension(t *testing.T) {
should := require.New(t)
RegisterExtension(func(type_ reflect.Type, field *reflect.StructField) ([]string, EncoderFunc, DecoderFunc) {
if type_.String() == "jsoniter.TestObject1" && field.Name == "field1" {
encode := func(ptr unsafe.Pointer, stream *Stream) {
str := *((*string)(ptr))
val, _ := strconv.Atoi(str)
stream.WriteInt(val)
}
decode := func(ptr unsafe.Pointer, iter *Iterator) {
*((*string)(ptr)) = strconv.Itoa(iter.ReadInt())
}
return []string{"field-1"}, encode, decode
}
return nil, nil, nil
})
RegisterExtension(&testExtension{})
obj := TestObject1{}
err := UnmarshalFromString(`{"field-1": 100}`, &obj)
should.Nil(err)
@ -111,24 +118,24 @@ func Test_customize_field_by_extension(t *testing.T) {
should.Equal(`{"field-1":100}`, str)
}
func Test_unexported_fields(t *testing.T) {
jsoniter := Config{SupportUnexportedStructFields: true}.Froze()
should := require.New(t)
type TestObject struct {
field1 string
field2 string `json:"field-2"`
}
obj := TestObject{}
obj.field1 = "hello"
should.Nil(jsoniter.UnmarshalFromString(`{}`, &obj))
should.Equal("hello", obj.field1)
should.Nil(jsoniter.UnmarshalFromString(`{"field1": "world", "field-2": "abc"}`, &obj))
should.Equal("world", obj.field1)
should.Equal("abc", obj.field2)
str, err := jsoniter.MarshalToString(obj)
should.Nil(err)
should.Contains(str, `"field-2":"abc"`)
}
//func Test_unexported_fields(t *testing.T) {
// jsoniter := Config{SupportUnexportedStructFields: true}.Froze()
// should := require.New(t)
// type TestObject struct {
// field1 string
// field2 string `json:"field-2"`
// }
// obj := TestObject{}
// obj.field1 = "hello"
// should.Nil(jsoniter.UnmarshalFromString(`{}`, &obj))
// should.Equal("hello", obj.field1)
// should.Nil(jsoniter.UnmarshalFromString(`{"field1": "world", "field-2": "abc"}`, &obj))
// should.Equal("world", obj.field1)
// should.Equal("abc", obj.field2)
// str, err := jsoniter.MarshalToString(obj)
// should.Nil(err)
// should.Contains(str, `"field-2":"abc"`)
//}
type ObjectImplementedMarshaler int
@ -155,6 +162,7 @@ func Test_marshaler_and_encoder(t *testing.T) {
type TestObject struct {
Field *ObjectImplementedMarshaler
}
ConfigDefault.cleanEncoders()
should := require.New(t)
RegisterTypeEncoderFunc("jsoniter.ObjectImplementedMarshaler", func(ptr unsafe.Pointer, stream *Stream) {
stream.WriteString("hello from encoder")
@ -198,6 +206,7 @@ func Test_unmarshaler_and_decoder(t *testing.T) {
Field *ObjectImplementedUnmarshaler
Field2 string
}
ConfigDefault.cleanDecoders()
should := require.New(t)
RegisterTypeDecoderFunc("jsoniter.ObjectImplementedUnmarshaler", func(ptr unsafe.Pointer, iter *Iterator) {
*(*ObjectImplementedUnmarshaler)(ptr) = 10