You've already forked json-iterator
							
							
				mirror of
				https://github.com/json-iterator/go.git
				synced 2025-10-31 00:07:40 +02:00 
			
		
		
		
	#23 hide unexported fields by default
This commit is contained in:
		| @@ -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{}{}, | ||||||
|   | |||||||
| @@ -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) | ||||||
|  | } | ||||||
| @@ -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) { | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user