1
0
mirror of https://github.com/json-iterator/go.git synced 2025-06-06 22:36:25 +02:00

#23 hide unexported fields by default

This commit is contained in:
Tao Wen 2017-05-24 09:39:11 +08:00
parent 5fbe4e387d
commit 6126a6d3ca
3 changed files with 113 additions and 84 deletions

View File

@ -6,32 +6,23 @@ import (
"reflect" "reflect"
"unsafe" "unsafe"
"strings" "strings"
"unicode"
) )
func encoderOfStruct(typ reflect.Type) (Encoder, error) { func encoderOfStruct(typ reflect.Type) (Encoder, error) {
structEncoder_ := &structEncoder{} structEncoder_ := &structEncoder{}
for i := 0; i < typ.NumField(); i++ { for i := 0; i < typ.NumField(); i++ {
field := typ.Field(i) field := typ.Field(i)
var fieldNames []string var extensionProvidedFieldNames []string
for _, extension := range extensions { for _, extension := range extensions {
alternativeFieldNames, _ := extension(typ, &field) alternativeFieldNames, _ := extension(typ, &field)
if alternativeFieldNames != nil { if alternativeFieldNames != nil {
fieldNames = alternativeFieldNames extensionProvidedFieldNames = alternativeFieldNames
} }
} }
tagParts := strings.Split(field.Tag.Get("json"), ",") tagParts := strings.Split(field.Tag.Get("json"), ",")
// if fieldNames set by extension, use theirs, otherwise try tags // if fieldNames set by extension, use theirs, otherwise try tags
if fieldNames == nil { fieldNames := calcFieldNames(field.Name, tagParts[0], extensionProvidedFieldNames)
/// tagParts[0] always present, even if no tags
switch tagParts[0] {
case "":
fieldNames = []string{field.Name}
case "-":
fieldNames = []string{}
default:
fieldNames = []string{tagParts[0]}
}
}
omitempty := false omitempty := false
for _, tagPart := range tagParts { for _, tagPart := range tagParts {
if tagPart == "omitempty" { if tagPart == "omitempty" {
@ -66,11 +57,11 @@ func decoderOfStruct(typ reflect.Type) (Decoder, error) {
for i := 0; i < typ.NumField(); i++ { for i := 0; i < typ.NumField(); i++ {
field := typ.Field(i) field := typ.Field(i)
fieldDecoderKey := fmt.Sprintf("%s/%s", typ.String(), field.Name) fieldDecoderKey := fmt.Sprintf("%s/%s", typ.String(), field.Name)
var fieldNames []string var extensionProviedFieldNames []string
for _, extension := range extensions { for _, extension := range extensions {
alternativeFieldNames, fun := extension(typ, &field) alternativeFieldNames, fun := extension(typ, &field)
if alternativeFieldNames != nil { if alternativeFieldNames != nil {
fieldNames = alternativeFieldNames extensionProviedFieldNames = alternativeFieldNames
} }
if fun != nil { if fun != nil {
fieldDecoders[fieldDecoderKey] = &funcDecoder{fun} fieldDecoders[fieldDecoderKey] = &funcDecoder{fun}
@ -78,18 +69,7 @@ func decoderOfStruct(typ reflect.Type) (Decoder, error) {
} }
decoder := fieldDecoders[fieldDecoderKey] decoder := fieldDecoders[fieldDecoderKey]
tagParts := strings.Split(field.Tag.Get("json"), ",") tagParts := strings.Split(field.Tag.Get("json"), ",")
// if fieldNames set by extension, use theirs, otherwise try tags fieldNames := calcFieldNames(field.Name, tagParts[0], extensionProviedFieldNames)
if fieldNames == nil {
/// tagParts[0] always present, even if no tags
switch tagParts[0] {
case "":
fieldNames = []string{field.Name}
case "-":
fieldNames = []string{}
default:
fieldNames = []string{tagParts[0]}
}
}
if decoder == nil && len(fieldNames) > 0 { if decoder == nil && len(fieldNames) > 0 {
var err error var err error
decoder, err = decoderOfType(field.Type) decoder, err = decoderOfType(field.Type)
@ -107,6 +87,36 @@ func decoderOfStruct(typ reflect.Type) (Decoder, error) {
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 EnableUnexportedStructFieldsSupport() {
RegisterExtension(func(type_ reflect.Type, field *reflect.StructField) ([]string, DecoderFunc) {
return []string{field.Name}, nil
})
}
func createStructDecoder(typ reflect.Type, fields map[string]*structFieldDecoder) (Decoder, error) { func createStructDecoder(typ reflect.Type, fields map[string]*structFieldDecoder) (Decoder, error) {
knownHash := map[int32]struct{}{ knownHash := map[int32]struct{}{
0: struct{}{}, 0: struct{}{},

View File

@ -103,3 +103,22 @@ func Test_customize_field_by_extension(t *testing.T) {
t.Fatal(obj.field1) t.Fatal(obj.field1)
} }
} }
func Test_unexported_fields(t *testing.T) {
EnableUnexportedStructFieldsSupport()
should := require.New(t)
type TestObject struct {
field1 string
field2 string `json:"field-2"`
}
obj := TestObject{}
obj.field1 = "hello"
should.Nil(UnmarshalFromString(`{}`, &obj))
should.Equal("hello", obj.field1)
should.Nil(UnmarshalFromString(`{"field1": "world", "field-2": "abc"}`, &obj))
should.Equal("world", obj.field1)
should.Equal("abc", obj.field2)
str, err := MarshalToString(obj)
should.Nil(err)
should.Equal(`{"field1":"world","field-2":"abc"}`, str)
}

View File

@ -9,106 +9,106 @@ import (
func Test_decode_one_field_struct(t *testing.T) { func Test_decode_one_field_struct(t *testing.T) {
should := require.New(t) should := require.New(t)
type TestObject struct { type TestObject struct {
field1 string Field1 string
} }
obj := TestObject{} obj := TestObject{}
should.Nil(UnmarshalFromString(`{}`, &obj)) should.Nil(UnmarshalFromString(`{}`, &obj))
should.Equal("", obj.field1) should.Equal("", obj.Field1)
should.Nil(UnmarshalFromString(`{"field1": "hello"}`, &obj)) should.Nil(UnmarshalFromString(`{"Field1": "hello"}`, &obj))
should.Equal("hello", obj.field1) should.Equal("hello", obj.Field1)
} }
func Test_decode_two_fields_struct(t *testing.T) { func Test_decode_two_fields_struct(t *testing.T) {
should := require.New(t) should := require.New(t)
type TestObject struct { type TestObject struct {
field1 string Field1 string
field2 string Field2 string
} }
obj := TestObject{} obj := TestObject{}
should.Nil(UnmarshalFromString(`{}`, &obj)) should.Nil(UnmarshalFromString(`{}`, &obj))
should.Equal("", obj.field1) should.Equal("", obj.Field1)
should.Nil(UnmarshalFromString(`{"field1": "a", "field2": "b"}`, &obj)) should.Nil(UnmarshalFromString(`{"Field1": "a", "Field2": "b"}`, &obj))
should.Equal("a", obj.field1) should.Equal("a", obj.Field1)
should.Equal("b", obj.field2) should.Equal("b", obj.Field2)
} }
func Test_decode_three_fields_struct(t *testing.T) { func Test_decode_three_fields_struct(t *testing.T) {
should := require.New(t) should := require.New(t)
type TestObject struct { type TestObject struct {
field1 string Field1 string
field2 string Field2 string
field3 string Field3 string
} }
obj := TestObject{} obj := TestObject{}
should.Nil(UnmarshalFromString(`{}`, &obj)) should.Nil(UnmarshalFromString(`{}`, &obj))
should.Equal("", obj.field1) should.Equal("", obj.Field1)
should.Nil(UnmarshalFromString(`{"field1": "a", "field2": "b", "field3": "c"}`, &obj)) should.Nil(UnmarshalFromString(`{"Field1": "a", "Field2": "b", "Field3": "c"}`, &obj))
should.Equal("a", obj.field1) should.Equal("a", obj.Field1)
should.Equal("b", obj.field2) should.Equal("b", obj.Field2)
should.Equal("c", obj.field3) should.Equal("c", obj.Field3)
} }
func Test_decode_four_fields_struct(t *testing.T) { func Test_decode_four_fields_struct(t *testing.T) {
should := require.New(t) should := require.New(t)
type TestObject struct { type TestObject struct {
field1 string Field1 string
field2 string Field2 string
field3 string Field3 string
field4 string Field4 string
} }
obj := TestObject{} obj := TestObject{}
should.Nil(UnmarshalFromString(`{}`, &obj)) should.Nil(UnmarshalFromString(`{}`, &obj))
should.Equal("", obj.field1) should.Equal("", obj.Field1)
should.Nil(UnmarshalFromString(`{"field1": "a", "field2": "b", "field3": "c", "field4": "d"}`, &obj)) should.Nil(UnmarshalFromString(`{"Field1": "a", "Field2": "b", "Field3": "c", "Field4": "d"}`, &obj))
should.Equal("a", obj.field1) should.Equal("a", obj.Field1)
should.Equal("b", obj.field2) should.Equal("b", obj.Field2)
should.Equal("c", obj.field3) should.Equal("c", obj.Field3)
should.Equal("d", obj.field4) should.Equal("d", obj.Field4)
} }
func Test_decode_five_fields_struct(t *testing.T) { func Test_decode_five_fields_struct(t *testing.T) {
should := require.New(t) should := require.New(t)
type TestObject struct { type TestObject struct {
field1 string Field1 string
field2 string Field2 string
field3 string Field3 string
field4 string Field4 string
field5 string Field5 string
} }
obj := TestObject{} obj := TestObject{}
should.Nil(UnmarshalFromString(`{}`, &obj)) should.Nil(UnmarshalFromString(`{}`, &obj))
should.Equal("", obj.field1) should.Equal("", obj.Field1)
should.Nil(UnmarshalFromString(`{"field1": "a", "field2": "b", "field3": "c", "field4": "d", "field5": "e"}`, &obj)) should.Nil(UnmarshalFromString(`{"Field1": "a", "Field2": "b", "Field3": "c", "Field4": "d", "Field5": "e"}`, &obj))
should.Equal("a", obj.field1) should.Equal("a", obj.Field1)
should.Equal("b", obj.field2) should.Equal("b", obj.Field2)
should.Equal("c", obj.field3) should.Equal("c", obj.Field3)
should.Equal("d", obj.field4) should.Equal("d", obj.Field4)
should.Equal("e", obj.field5) should.Equal("e", obj.Field5)
} }
func Test_decode_ten_fields_struct(t *testing.T) { func Test_decode_ten_fields_struct(t *testing.T) {
should := require.New(t) should := require.New(t)
type TestObject struct { type TestObject struct {
field1 string Field1 string
field2 string Field2 string
field3 string Field3 string
field4 string Field4 string
field5 string Field5 string
field6 string Field6 string
field7 string Field7 string
field8 string Field8 string
field9 string Field9 string
field10 string Field10 string
} }
obj := TestObject{} obj := TestObject{}
should.Nil(UnmarshalFromString(`{}`, &obj)) should.Nil(UnmarshalFromString(`{}`, &obj))
should.Equal("", obj.field1) should.Equal("", obj.Field1)
should.Nil(UnmarshalFromString(`{"field1": "a", "field2": "b", "field3": "c", "field4": "d", "field5": "e"}`, &obj)) should.Nil(UnmarshalFromString(`{"Field1": "a", "Field2": "b", "Field3": "c", "Field4": "d", "Field5": "e"}`, &obj))
should.Equal("a", obj.field1) should.Equal("a", obj.Field1)
should.Equal("b", obj.field2) should.Equal("b", obj.Field2)
should.Equal("c", obj.field3) should.Equal("c", obj.Field3)
should.Equal("d", obj.field4) should.Equal("d", obj.Field4)
should.Equal("e", obj.field5) should.Equal("e", obj.Field5)
} }
func Test_decode_struct_field_with_tag(t *testing.T) { func Test_decode_struct_field_with_tag(t *testing.T) {