Add interface{} support (#341)
This commit is contained in:
+40
-9
@@ -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
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user