1
0
mirror of https://github.com/json-iterator/go.git synced 2025-04-20 11:28: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 { type Config struct {
IndentionStep int IndentionStep int
MarshalFloatWith6Digits bool MarshalFloatWith6Digits bool
SupportUnexportedStructFields bool
EscapeHtml bool EscapeHtml bool
SortMapKeys bool SortMapKeys bool
UseNumber bool UseNumber bool
@ -24,7 +23,7 @@ type frozenConfig struct {
indentionStep int indentionStep int
decoderCache unsafe.Pointer decoderCache unsafe.Pointer
encoderCache unsafe.Pointer encoderCache unsafe.Pointer
extensions []ExtensionFunc extensions []Extension
streamPool chan *Stream streamPool chan *Stream
iteratorPool chan *Iterator iteratorPool chan *Iterator
} }
@ -65,9 +64,6 @@ func (cfg Config) Froze() *frozenConfig {
if cfg.MarshalFloatWith6Digits { if cfg.MarshalFloatWith6Digits {
frozenConfig.marshalFloatWith6Digits() frozenConfig.marshalFloatWith6Digits()
} }
if cfg.SupportUnexportedStructFields {
frozenConfig.supportUnexportedStructFields()
}
if cfg.EscapeHtml { if cfg.EscapeHtml {
frozenConfig.escapeHtml() frozenConfig.escapeHtml()
} }
@ -88,17 +84,10 @@ func (cfg *frozenConfig) useNumber() {
}}) }})
} }
// RegisterExtension can register a custom extension func (cfg *frozenConfig) registerExtension(extension Extension) {
func (cfg *frozenConfig) registerExtension(extension ExtensionFunc) {
cfg.extensions = append(cfg.extensions, 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 { type lossyFloat32Encoder struct {
} }

View File

@ -4,28 +4,54 @@ import (
"reflect" "reflect"
"fmt" "fmt"
"unsafe" "unsafe"
"strings"
"unicode"
) )
var typeDecoders map[string]ValDecoder var typeDecoders = map[string]ValDecoder{}
var fieldDecoders map[string]ValDecoder var fieldDecoders = map[string]ValDecoder{}
var typeEncoders map[string]ValEncoder var typeEncoders = map[string]ValEncoder{}
var fieldEncoders map[string]ValEncoder var fieldEncoders = map[string]ValEncoder{}
var extensions []ExtensionFunc 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 { type funcDecoder struct {
fun DecoderFunc 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) { func RegisterTypeDecoderFunc(typ string, fun DecoderFunc) {
typeDecoders[typ] = &funcDecoder{fun} typeDecoders[typ] = &funcDecoder{fun}
} }
@ -58,36 +84,134 @@ func RegisterFieldEncoder(typ string, field string, encoder ValEncoder) {
fieldEncoders[fmt.Sprintf("%s/%s", typ, field)] = encoder fieldEncoders[fmt.Sprintf("%s/%s", typ, field)] = encoder
} }
func RegisterExtension(extension ExtensionFunc) { func RegisterExtension(extension Extension) {
extensions = append(extensions, extension) extensions = append(extensions, extension)
} }
func getTypeDecoderFromExtension(typ reflect.Type) ValDecoder { func getTypeDecoderFromExtension(typ reflect.Type) ValDecoder {
for _, extension := range extensions {
decoder := extension.CreateDecoder(typ)
if decoder != nil {
return decoder
}
}
typeName := typ.String() typeName := typ.String()
typeDecoder := typeDecoders[typeName] decoder := typeDecoders[typeName]
if typeDecoder != nil { if decoder != nil {
return typeDecoder return decoder
} }
if typ.Kind() == reflect.Ptr { if typ.Kind() == reflect.Ptr {
typeDecoder := typeDecoders[typ.Elem().String()] decoder := typeDecoders[typ.Elem().String()]
if typeDecoder != nil { if decoder != nil {
return &optionalDecoder{typ.Elem(), typeDecoder} return &optionalDecoder{typ.Elem(), decoder}
} }
} }
return nil return nil
} }
func getTypeEncoderFromExtension(typ reflect.Type) ValEncoder { func getTypeEncoderFromExtension(typ reflect.Type) ValEncoder {
for _, extension := range extensions {
encoder := extension.CreateEncoder(typ)
if encoder != nil {
return encoder
}
}
typeName := typ.String() typeName := typ.String()
typeEncoder := typeEncoders[typeName] encoder := typeEncoders[typeName]
if typeEncoder != nil { if encoder != nil {
return typeEncoder return encoder
} }
if typ.Kind() == reflect.Ptr { if typ.Kind() == reflect.Ptr {
typeEncoder := typeEncoders[typ.Elem().String()] encoder := typeEncoders[typ.Elem().String()]
if typeEncoder != nil { if encoder != nil {
return &optionalEncoder{typeEncoder} return &optionalEncoder{encoder}
} }
} }
return nil 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" "fmt"
"io" "io"
"reflect" "reflect"
"strings"
"unicode"
"unsafe" "unsafe"
) )
func encoderOfStruct(cfg *frozenConfig, typ reflect.Type) (ValEncoder, error) { func encoderOfStruct(cfg *frozenConfig, typ reflect.Type) (ValEncoder, error) {
structEncoder_ := &structEncoder{} structEncoder_ := &structEncoder{}
fields := map[string]*structFieldEncoder{} fields := map[string]*structFieldEncoder{}
for _, field := range listStructFields(typ) { structDescriptor, err := describeStruct(cfg, 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 { if err != nil {
return prefix(fmt.Sprintf("{%s}", field.Name)).addToEncoder(encoder, err) return nil, err
} }
// map is stored as pointer in the struct for _, binding := range structDescriptor.Fields {
if field.Type.Kind() == reflect.Map { for _, fieldName := range binding.ToNames {
encoder = &optionalEncoder{encoder} fields[fieldName] = &structFieldEncoder{binding.Field, fieldName, binding.Encoder, binding.ShouldOmitEmpty}
}
}
if stringMode {
encoder = &stringModeEncoder{encoder}
}
for _, fieldName := range fieldNames {
fields[fieldName] = &structFieldEncoder{field, fieldName, encoder, omitempty}
} }
} }
if len(fields) == 0 { if len(fields) == 0 {
@ -73,88 +28,20 @@ func encoderOfStruct(cfg *frozenConfig, typ reflect.Type) (ValEncoder, error) {
return structEncoder_, nil 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) { func decoderOfStruct(cfg *frozenConfig, typ reflect.Type) (ValDecoder, error) {
fields := map[string]*structFieldDecoder{} fields := map[string]*structFieldDecoder{}
for _, field := range listStructFields(typ) { structDescriptor, err := describeStruct(cfg, 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 { if err != nil {
return prefix(fmt.Sprintf("{%s}", field.Name)).addToDecoder(decoder, err) return nil, err
} }
} for _, binding := range structDescriptor.Fields {
for _, tagPart := range tagParts[1:] { for _, fieldName := range binding.FromNames {
if tagPart == "string" { fields[fieldName] = &structFieldDecoder{binding.Field, binding.Decoder}
decoder = &stringModeDecoder{decoder}
}
}
for _, fieldName := range fieldNames {
fields[fieldName] = &structFieldDecoder{field, decoder}
} }
} }
return createStructDecoder(typ, fields) 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) { func createStructDecoder(typ reflect.Type, fields map[string]*structFieldDecoder) (ValDecoder, error) {
knownHash := map[int32]struct{}{ knownHash := map[int32]struct{}{
0: {}, 0: {},

View File

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