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++ {
|
for i := 0; i < mtype.NumField(); i++ {
|
||||||
mtypef, mvalf := mtype.Field(i), mval.Field(i)
|
mtypef, mvalf := mtype.Field(i), mval.Field(i)
|
||||||
opts := tomlOptions(mtypef, e.annotation)
|
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)
|
val, err := e.valueToToml(mtypef.Type, mvalf)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@@ -358,6 +358,9 @@ func (e *Encoder) valueToTree(mtype reflect.Type, mval reflect.Value) (*Tree, er
|
|||||||
}
|
}
|
||||||
for _, key := range keys {
|
for _, key := range keys {
|
||||||
mvalf := mval.MapIndex(key)
|
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)
|
val, err := e.valueToToml(mtype.Elem(), mvalf)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
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
|
// Convert given marshal slice to slice of toml values
|
||||||
func (e *Encoder) valueToOtherSlice(mtype reflect.Type, mval reflect.Value) (interface{}, error) {
|
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())
|
tval := make([]interface{}, mval.Len(), mval.Len())
|
||||||
for i := 0; i < mval.Len(); i++ {
|
for i := 0; i < mval.Len(); i++ {
|
||||||
val, err := e.valueToToml(mtype.Elem(), mval.Index(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 {
|
if mtype.Kind() == reflect.Ptr {
|
||||||
return e.valueToToml(mtype.Elem(), mval.Elem())
|
return e.valueToToml(mtype.Elem(), mval.Elem())
|
||||||
}
|
}
|
||||||
|
if mtype.Kind() == reflect.Interface {
|
||||||
|
return e.valueToToml(mval.Elem().Type(), mval.Elem())
|
||||||
|
}
|
||||||
switch {
|
switch {
|
||||||
case isCustomMarshaler(mtype):
|
case isCustomMarshaler(mtype):
|
||||||
return callCustomMarshaler(mval)
|
return callCustomMarshaler(mval)
|
||||||
@@ -699,13 +708,12 @@ func (d *Decoder) valueFromToml(mtype reflect.Type, tval interface{}, mval1 *ref
|
|||||||
return d.valueFromTree(mtype, t, mval11)
|
return d.valueFromTree(mtype, t, mval11)
|
||||||
}
|
}
|
||||||
|
|
||||||
if mtype.Kind() == reflect.Interface &&
|
if mtype.Kind() == reflect.Interface {
|
||||||
mval1.Elem().Kind() == reflect.Ptr &&
|
if mval1 == nil || mval1.IsNil() {
|
||||||
reflect.TypeOf(mval1.Elem()).Kind() == reflect.Struct {
|
return d.valueFromTree(reflect.TypeOf(map[string]interface{}{}), t, nil)
|
||||||
|
} else {
|
||||||
mval111 := reflect.ValueOf(mval1.Interface()).Elem()
|
return d.valueFromToml(mval1.Elem().Type(), t, nil)
|
||||||
mval11 = &mval111
|
}
|
||||||
return d.valueFromTree(reflect.TypeOf(mval1.Interface()).Elem(), t, mval11)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return reflect.ValueOf(nil), fmt.Errorf("Can't convert %v(%T) to a tree", tval, tval)
|
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) {
|
if isTreeSequence(mtype) {
|
||||||
return d.valueFromTreeSlice(mtype, t)
|
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)
|
return reflect.ValueOf(nil), fmt.Errorf("Can't convert %v(%T) to trees", tval, tval)
|
||||||
case []interface{}:
|
case []interface{}:
|
||||||
if isOtherSequence(mtype) {
|
if isOtherSequence(mtype) {
|
||||||
return d.valueFromOtherSlice(mtype, t)
|
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)
|
return reflect.ValueOf(nil), fmt.Errorf("Can't convert %v(%T) to a slice", tval, tval)
|
||||||
default:
|
default:
|
||||||
switch mtype.Kind() {
|
switch mtype.Kind() {
|
||||||
@@ -802,6 +826,13 @@ func (d *Decoder) valueFromToml(mtype reflect.Type, tval interface{}, mval1 *ref
|
|||||||
}
|
}
|
||||||
|
|
||||||
return val.Convert(mtype), nil
|
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:
|
default:
|
||||||
return reflect.ValueOf(nil), fmt.Errorf("Can't convert %v(%T) to %v(%v)", tval, tval, mtype, mtype.Kind())
|
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) {
|
func (d *Decoder) unwrapPointer(mtype reflect.Type, tval interface{}, mval1 *reflect.Value) (reflect.Value, error) {
|
||||||
var melem *reflect.Value
|
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()
|
elem := mval1.Elem()
|
||||||
melem = &elem
|
melem = &elem
|
||||||
}
|
}
|
||||||
|
|||||||
+238
-1
@@ -184,7 +184,7 @@ func TestInterface(t *testing.T) {
|
|||||||
expected := Conf{
|
expected := Conf{
|
||||||
Name: "rui",
|
Name: "rui",
|
||||||
Age: 18,
|
Age: 18,
|
||||||
Inter: NestedStruct{
|
Inter: &NestedStruct{
|
||||||
FirstName: "wang",
|
FirstName: "wang",
|
||||||
LastName: "jl",
|
LastName: "jl",
|
||||||
Age: 100,
|
Age: 100,
|
||||||
@@ -2437,3 +2437,240 @@ age = 222`), &server); err == nil {
|
|||||||
t.Fatalf("unexpected error: %v", err)
|
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