diff --git a/feature_reflect_object.go b/feature_reflect_object.go index 5dc9125..722567a 100644 --- a/feature_reflect_object.go +++ b/feature_reflect_object.go @@ -6,32 +6,23 @@ import ( "reflect" "unsafe" "strings" + "unicode" ) func encoderOfStruct(typ reflect.Type) (Encoder, error) { structEncoder_ := &structEncoder{} for i := 0; i < typ.NumField(); i++ { field := typ.Field(i) - var fieldNames []string + var extensionProvidedFieldNames []string for _, extension := range extensions { alternativeFieldNames, _ := extension(typ, &field) if alternativeFieldNames != nil { - fieldNames = alternativeFieldNames + extensionProvidedFieldNames = alternativeFieldNames } } tagParts := strings.Split(field.Tag.Get("json"), ",") // if fieldNames set by extension, use theirs, otherwise try tags - 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]} - } - } + fieldNames := calcFieldNames(field.Name, tagParts[0], extensionProvidedFieldNames) omitempty := false for _, tagPart := range tagParts { if tagPart == "omitempty" { @@ -66,11 +57,11 @@ func decoderOfStruct(typ reflect.Type) (Decoder, error) { for i := 0; i < typ.NumField(); i++ { field := typ.Field(i) fieldDecoderKey := fmt.Sprintf("%s/%s", typ.String(), field.Name) - var fieldNames []string + var extensionProviedFieldNames []string for _, extension := range extensions { alternativeFieldNames, fun := extension(typ, &field) if alternativeFieldNames != nil { - fieldNames = alternativeFieldNames + extensionProviedFieldNames = alternativeFieldNames } if fun != nil { fieldDecoders[fieldDecoderKey] = &funcDecoder{fun} @@ -78,18 +69,7 @@ func decoderOfStruct(typ reflect.Type) (Decoder, error) { } decoder := fieldDecoders[fieldDecoderKey] tagParts := strings.Split(field.Tag.Get("json"), ",") - // if fieldNames set by extension, use theirs, otherwise try tags - 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]} - } - } + fieldNames := calcFieldNames(field.Name, tagParts[0], extensionProviedFieldNames) if decoder == nil && len(fieldNames) > 0 { var err error decoder, err = decoderOfType(field.Type) @@ -107,6 +87,36 @@ func decoderOfStruct(typ reflect.Type) (Decoder, error) { 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) { knownHash := map[int32]struct{}{ 0: struct{}{}, diff --git a/jsoniter_customize_test.go b/jsoniter_customize_test.go index 72d276a..9b502a9 100644 --- a/jsoniter_customize_test.go +++ b/jsoniter_customize_test.go @@ -103,3 +103,22 @@ func Test_customize_field_by_extension(t *testing.T) { 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) +} \ No newline at end of file diff --git a/jsoniter_reflect_struct_test.go b/jsoniter_reflect_struct_test.go index e861cdd..a5f1892 100644 --- a/jsoniter_reflect_struct_test.go +++ b/jsoniter_reflect_struct_test.go @@ -9,106 +9,106 @@ import ( func Test_decode_one_field_struct(t *testing.T) { should := require.New(t) type TestObject struct { - field1 string + Field1 string } obj := TestObject{} should.Nil(UnmarshalFromString(`{}`, &obj)) - should.Equal("", obj.field1) - should.Nil(UnmarshalFromString(`{"field1": "hello"}`, &obj)) - should.Equal("hello", obj.field1) + should.Equal("", obj.Field1) + should.Nil(UnmarshalFromString(`{"Field1": "hello"}`, &obj)) + should.Equal("hello", obj.Field1) } func Test_decode_two_fields_struct(t *testing.T) { should := require.New(t) type TestObject struct { - field1 string - field2 string + Field1 string + Field2 string } obj := TestObject{} should.Nil(UnmarshalFromString(`{}`, &obj)) - should.Equal("", obj.field1) - should.Nil(UnmarshalFromString(`{"field1": "a", "field2": "b"}`, &obj)) - should.Equal("a", obj.field1) - should.Equal("b", obj.field2) + should.Equal("", obj.Field1) + should.Nil(UnmarshalFromString(`{"Field1": "a", "Field2": "b"}`, &obj)) + should.Equal("a", obj.Field1) + should.Equal("b", obj.Field2) } func Test_decode_three_fields_struct(t *testing.T) { should := require.New(t) type TestObject struct { - field1 string - field2 string - field3 string + Field1 string + Field2 string + Field3 string } obj := TestObject{} should.Nil(UnmarshalFromString(`{}`, &obj)) - should.Equal("", obj.field1) - should.Nil(UnmarshalFromString(`{"field1": "a", "field2": "b", "field3": "c"}`, &obj)) - should.Equal("a", obj.field1) - should.Equal("b", obj.field2) - should.Equal("c", obj.field3) + should.Equal("", obj.Field1) + should.Nil(UnmarshalFromString(`{"Field1": "a", "Field2": "b", "Field3": "c"}`, &obj)) + should.Equal("a", obj.Field1) + should.Equal("b", obj.Field2) + should.Equal("c", obj.Field3) } func Test_decode_four_fields_struct(t *testing.T) { should := require.New(t) type TestObject struct { - field1 string - field2 string - field3 string - field4 string + Field1 string + Field2 string + Field3 string + Field4 string } obj := TestObject{} should.Nil(UnmarshalFromString(`{}`, &obj)) - should.Equal("", obj.field1) - should.Nil(UnmarshalFromString(`{"field1": "a", "field2": "b", "field3": "c", "field4": "d"}`, &obj)) - should.Equal("a", obj.field1) - should.Equal("b", obj.field2) - should.Equal("c", obj.field3) - should.Equal("d", obj.field4) + should.Equal("", obj.Field1) + should.Nil(UnmarshalFromString(`{"Field1": "a", "Field2": "b", "Field3": "c", "Field4": "d"}`, &obj)) + should.Equal("a", obj.Field1) + should.Equal("b", obj.Field2) + should.Equal("c", obj.Field3) + should.Equal("d", obj.Field4) } func Test_decode_five_fields_struct(t *testing.T) { should := require.New(t) type TestObject struct { - field1 string - field2 string - field3 string - field4 string - field5 string + Field1 string + Field2 string + Field3 string + Field4 string + Field5 string } obj := TestObject{} should.Nil(UnmarshalFromString(`{}`, &obj)) - should.Equal("", obj.field1) - should.Nil(UnmarshalFromString(`{"field1": "a", "field2": "b", "field3": "c", "field4": "d", "field5": "e"}`, &obj)) - should.Equal("a", obj.field1) - should.Equal("b", obj.field2) - should.Equal("c", obj.field3) - should.Equal("d", obj.field4) - should.Equal("e", obj.field5) + should.Equal("", obj.Field1) + should.Nil(UnmarshalFromString(`{"Field1": "a", "Field2": "b", "Field3": "c", "Field4": "d", "Field5": "e"}`, &obj)) + should.Equal("a", obj.Field1) + should.Equal("b", obj.Field2) + should.Equal("c", obj.Field3) + should.Equal("d", obj.Field4) + should.Equal("e", obj.Field5) } func Test_decode_ten_fields_struct(t *testing.T) { should := require.New(t) type TestObject struct { - field1 string - field2 string - field3 string - field4 string - field5 string - field6 string - field7 string - field8 string - field9 string - field10 string + Field1 string + Field2 string + Field3 string + Field4 string + Field5 string + Field6 string + Field7 string + Field8 string + Field9 string + Field10 string } obj := TestObject{} should.Nil(UnmarshalFromString(`{}`, &obj)) - should.Equal("", obj.field1) - should.Nil(UnmarshalFromString(`{"field1": "a", "field2": "b", "field3": "c", "field4": "d", "field5": "e"}`, &obj)) - should.Equal("a", obj.field1) - should.Equal("b", obj.field2) - should.Equal("c", obj.field3) - should.Equal("d", obj.field4) - should.Equal("e", obj.field5) + should.Equal("", obj.Field1) + should.Nil(UnmarshalFromString(`{"Field1": "a", "Field2": "b", "Field3": "c", "Field4": "d", "Field5": "e"}`, &obj)) + should.Equal("a", obj.Field1) + should.Equal("b", obj.Field2) + should.Equal("c", obj.Field3) + should.Equal("d", obj.Field4) + should.Equal("e", obj.Field5) } func Test_decode_struct_field_with_tag(t *testing.T) {