Add interface{} support (#341)

This commit is contained in:
Allen
2020-03-25 23:12:18 +08:00
committed by GitHub
parent 7ee1118b4b
commit eb7280e4a7
2 changed files with 278 additions and 10 deletions
+40 -9
View File
@@ -323,7 +323,7 @@ func (e *Encoder) valueToTree(mtype reflect.Type, mval reflect.Value) (*Tree, er
for i := 0; i < mtype.NumField(); i++ {
mtypef, mvalf := mtype.Field(i), mval.Field(i)
opts := tomlOptions(mtypef, e.annotation)
if opts.include && (!opts.omitempty || !isZero(mvalf)) {
if opts.include && ((mtypef.Type.Kind() != reflect.Interface && !opts.omitempty) || !isZero(mvalf)) {
val, err := e.valueToToml(mtypef.Type, mvalf)
if err != nil {
return nil, err
@@ -358,6 +358,9 @@ func (e *Encoder) valueToTree(mtype reflect.Type, mval reflect.Value) (*Tree, er
}
for _, key := range keys {
mvalf := mval.MapIndex(key)
if (mtype.Elem().Kind() == reflect.Ptr || mtype.Elem().Kind() == reflect.Interface) && mvalf.IsNil() {
continue
}
val, err := e.valueToToml(mtype.Elem(), mvalf)
if err != nil {
return nil, err
@@ -391,6 +394,9 @@ func (e *Encoder) valueToTreeSlice(mtype reflect.Type, mval reflect.Value) ([]*T
// Convert given marshal slice to slice of toml values
func (e *Encoder) valueToOtherSlice(mtype reflect.Type, mval reflect.Value) (interface{}, error) {
if mtype.Elem().Kind() == reflect.Interface {
return nil, fmt.Errorf("marshal can't handle []interface{}")
}
tval := make([]interface{}, mval.Len(), mval.Len())
for i := 0; i < mval.Len(); i++ {
val, err := e.valueToToml(mtype.Elem(), mval.Index(i))
@@ -408,6 +414,9 @@ func (e *Encoder) valueToToml(mtype reflect.Type, mval reflect.Value) (interface
if mtype.Kind() == reflect.Ptr {
return e.valueToToml(mtype.Elem(), mval.Elem())
}
if mtype.Kind() == reflect.Interface {
return e.valueToToml(mval.Elem().Type(), mval.Elem())
}
switch {
case isCustomMarshaler(mtype):
return callCustomMarshaler(mval)
@@ -699,13 +708,12 @@ func (d *Decoder) valueFromToml(mtype reflect.Type, tval interface{}, mval1 *ref
return d.valueFromTree(mtype, t, mval11)
}
if mtype.Kind() == reflect.Interface &&
mval1.Elem().Kind() == reflect.Ptr &&
reflect.TypeOf(mval1.Elem()).Kind() == reflect.Struct {
mval111 := reflect.ValueOf(mval1.Interface()).Elem()
mval11 = &mval111
return d.valueFromTree(reflect.TypeOf(mval1.Interface()).Elem(), t, mval11)
if mtype.Kind() == reflect.Interface {
if mval1 == nil || mval1.IsNil() {
return d.valueFromTree(reflect.TypeOf(map[string]interface{}{}), t, nil)
} else {
return d.valueFromToml(mval1.Elem().Type(), t, nil)
}
}
return reflect.ValueOf(nil), fmt.Errorf("Can't convert %v(%T) to a tree", tval, tval)
@@ -713,11 +721,27 @@ func (d *Decoder) valueFromToml(mtype reflect.Type, tval interface{}, mval1 *ref
if isTreeSequence(mtype) {
return d.valueFromTreeSlice(mtype, t)
}
if mtype.Kind() == reflect.Interface {
if mval1 == nil || mval1.IsNil() {
return d.valueFromTreeSlice(reflect.TypeOf([]map[string]interface{}{}), t)
} else {
ival := mval1.Elem()
return d.valueFromToml(mval1.Elem().Type(), t, &ival)
}
}
return reflect.ValueOf(nil), fmt.Errorf("Can't convert %v(%T) to trees", tval, tval)
case []interface{}:
if isOtherSequence(mtype) {
return d.valueFromOtherSlice(mtype, t)
}
if mtype.Kind() == reflect.Interface {
if mval1 == nil || mval1.IsNil() {
return d.valueFromOtherSlice(reflect.TypeOf([]interface{}{}), t)
} else {
ival := mval1.Elem()
return d.valueFromToml(mval1.Elem().Type(), t, &ival)
}
}
return reflect.ValueOf(nil), fmt.Errorf("Can't convert %v(%T) to a slice", tval, tval)
default:
switch mtype.Kind() {
@@ -802,6 +826,13 @@ func (d *Decoder) valueFromToml(mtype reflect.Type, tval interface{}, mval1 *ref
}
return val.Convert(mtype), nil
case reflect.Interface:
if mval1 == nil || mval1.IsNil() {
return reflect.ValueOf(tval), nil
} else {
ival := mval1.Elem()
return d.valueFromToml(mval1.Elem().Type(), t, &ival)
}
default:
return reflect.ValueOf(nil), fmt.Errorf("Can't convert %v(%T) to %v(%v)", tval, tval, mtype, mtype.Kind())
}
@@ -811,7 +842,7 @@ func (d *Decoder) valueFromToml(mtype reflect.Type, tval interface{}, mval1 *ref
func (d *Decoder) unwrapPointer(mtype reflect.Type, tval interface{}, mval1 *reflect.Value) (reflect.Value, error) {
var melem *reflect.Value
if mval1 != nil && !mval1.IsNil() && mtype.Elem().Kind() == reflect.Struct {
if mval1 != nil && !mval1.IsNil() && (mtype.Elem().Kind() == reflect.Struct || mtype.Elem().Kind() == reflect.Interface) {
elem := mval1.Elem()
melem = &elem
}
+238 -1
View File
@@ -184,7 +184,7 @@ func TestInterface(t *testing.T) {
expected := Conf{
Name: "rui",
Age: 18,
Inter: NestedStruct{
Inter: &NestedStruct{
FirstName: "wang",
LastName: "jl",
Age: 100,
@@ -2437,3 +2437,240 @@ age = 222`), &server); err == nil {
t.Fatalf("unexpected error: %v", err)
}
}
func TestMarshalInterface(t *testing.T) {
type InnerStruct struct {
InnerField string
}
type OuterStruct struct {
PrimitiveField interface{}
ArrayField interface{}
StructArrayField interface{}
MapField map[string]interface{}
StructField interface{}
PointerField interface{}
NilField interface{}
InterfacePointerField *interface{}
}
type ShouldNotSupportStruct struct {
InterfaceArray []interface{}
}
expected := []byte(`ArrayField = [1,2,3]
InterfacePointerField = "hello world"
PrimitiveField = "string"
[MapField]
key1 = "value1"
key2 = false
[MapField.key3]
InnerField = "value3"
[PointerField]
InnerField = "yyy"
[[StructArrayField]]
InnerField = "s1"
[[StructArrayField]]
InnerField = "s2"
[StructField]
InnerField = "xxx"
`)
var h interface{} = "hello world"
if result, err := Marshal(OuterStruct{
"string",
[]int{1, 2, 3},
[]InnerStruct{{"s1"}, {"s2"}},
map[string]interface{}{
"key1": "value1",
"key2": false,
"key3": InnerStruct{"value3"},
"nil value": nil,
},
InnerStruct{
"xxx",
},
&InnerStruct{
"yyy",
},
nil,
&h,
}); err == nil {
if !bytes.Equal(result, expected) {
t.Errorf("Bad marshal: expected\n----\n%s\n----\ngot\n----\n%s\n----\n", expected, result)
}
} else {
t.Fatal(err)
}
// according to the toml standard, data types of array may not be mixed
if _, err := Marshal(ShouldNotSupportStruct{[]interface{}{1, "a", true}}); err == nil {
t.Errorf("Should not support []interface{} marshaling")
}
}
func TestUnmarshalToNilInterface(t *testing.T) {
toml := []byte(`
PrimitiveField = "Hello"
ArrayField = [1,2,3]
InterfacePointerField = "World"
[StructField]
Field1 = 123
Field2 = "Field2"
[MapField]
MapField1 = [4,5,6]
MapField2 = {A = "A"}
MapField3 = false
[[StructArrayField]]
Name = "Allen"
Age = 20
[[StructArrayField]]
Name = "Jack"
Age = 23
`)
type OuterStruct struct {
PrimitiveField interface{}
ArrayField interface{}
StructArrayField interface{}
MapField map[string]interface{}
StructField interface{}
NilField interface{}
InterfacePointerField *interface{}
}
var s interface{} = "World"
expected := OuterStruct{
PrimitiveField: "Hello",
ArrayField: []interface{}{int64(1), int64(2), int64(3)},
StructField: map[string]interface{}{
"Field1": int64(123),
"Field2": "Field2",
},
MapField: map[string]interface{}{
"MapField1": []interface{}{int64(4), int64(5), int64(6)},
"MapField2": map[string]interface{}{
"A": "A",
},
"MapField3": false,
},
NilField: nil,
InterfacePointerField: &s,
StructArrayField: []map[string]interface{}{
{
"Name": "Allen",
"Age": int64(20),
},
{
"Name": "Jack",
"Age": int64(23),
},
},
}
actual := OuterStruct{}
if err := Unmarshal(toml, &actual); err == nil {
if !reflect.DeepEqual(actual, expected) {
t.Errorf("Bad unmarshal: expected %v, got %v", expected, actual)
}
} else {
t.Fatal(err)
}
}
func TestUnmarshalToNonNilInterface(t *testing.T) {
toml := []byte(`
PrimitiveField = "Allen"
ArrayField = [1,2,3]
[StructField]
InnerField = "After1"
[PointerField]
InnerField = "After2"
[InterfacePointerField]
InnerField = "After"
[MapField]
MapField1 = [4,5,6]
MapField2 = {A = "A"}
MapField3 = false
[[StructArrayField]]
InnerField = "After3"
[[StructArrayField]]
InnerField = "After4"
`)
type InnerStruct struct {
InnerField interface{}
}
type OuterStruct struct {
PrimitiveField interface{}
ArrayField interface{}
StructArrayField interface{}
MapField map[string]interface{}
StructField interface{}
PointerField interface{}
NilField interface{}
InterfacePointerField *interface{}
}
var s interface{} = InnerStruct{"After"}
expected := OuterStruct{
PrimitiveField: "Allen",
ArrayField: []int{1, 2, 3},
StructField: InnerStruct{InnerField: "After1"},
MapField: map[string]interface{}{
"MapField1": []interface{}{int64(4), int64(5), int64(6)},
"MapField2": map[string]interface{}{
"A": "A",
},
"MapField3": false,
},
PointerField: &InnerStruct{InnerField: "After2"},
NilField: nil,
InterfacePointerField: &s,
StructArrayField: []InnerStruct{
{InnerField: "After3"},
{InnerField: "After4"},
},
}
actual := OuterStruct{
PrimitiveField: "aaa",
ArrayField: []int{100, 200, 300, 400},
StructField: InnerStruct{InnerField: "Before1"},
MapField: map[string]interface{}{
"MapField1": []int{4, 5, 6},
"MapField2": map[string]string{
"B": "BBB",
},
"MapField3": true,
},
PointerField: &InnerStruct{InnerField: "Before2"},
NilField: nil,
InterfacePointerField: &s,
StructArrayField: []InnerStruct{
{InnerField: "Before3"},
{InnerField: "Before4"},
},
}
if err := Unmarshal(toml, &actual); err == nil {
if !reflect.DeepEqual(actual, expected) {
t.Errorf("Bad unmarshal: expected %v, got %v", expected, actual)
}
} else {
t.Fatal(err)
}
}