diff --git a/marshal.go b/marshal.go index d71255a..afe6c37 100644 --- a/marshal.go +++ b/marshal.go @@ -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 } diff --git a/marshal_test.go b/marshal_test.go index 4168b16..9b09992 100644 --- a/marshal_test.go +++ b/marshal_test.go @@ -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) + } +}