Encoder: try to use pointer type TextMarshaler (#679)
If a type does not implement the encoding.TextMarshaler interface but its pointer type does, use it if possible. Fixes #678
This commit is contained in:
+7
-2
@@ -209,7 +209,12 @@ func (enc *Encoder) encode(b []byte, ctx encoderCtx, v reflect.Value) ([]byte, e
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if v.Type().Implements(textMarshalerType) {
|
hasTextMarshaler := v.Type().Implements(textMarshalerType)
|
||||||
|
if hasTextMarshaler || (v.CanAddr() && reflect.PtrTo(v.Type()).Implements(textMarshalerType)) {
|
||||||
|
if !hasTextMarshaler {
|
||||||
|
v = v.Addr()
|
||||||
|
}
|
||||||
|
|
||||||
if ctx.isRoot() {
|
if ctx.isRoot() {
|
||||||
return nil, fmt.Errorf("toml: type %s implementing the TextMarshaler interface cannot be a root element", v.Type())
|
return nil, fmt.Errorf("toml: type %s implementing the TextMarshaler interface cannot be a root element", v.Type())
|
||||||
}
|
}
|
||||||
@@ -657,7 +662,7 @@ func willConvertToTable(ctx encoderCtx, v reflect.Value) bool {
|
|||||||
if !v.IsValid() {
|
if !v.IsValid() {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
if v.Type() == timeType || v.Type().Implements(textMarshalerType) {
|
if v.Type() == timeType || v.Type().Implements(textMarshalerType) || (v.Kind() != reflect.Ptr && v.CanAddr() && reflect.PtrTo(v.Type()).Implements(textMarshalerType)) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+43
-23
@@ -4,6 +4,7 @@ import (
|
|||||||
"bytes"
|
"bytes"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"math/big"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
@@ -798,6 +799,48 @@ func TestIssue590(t *testing.T) {
|
|||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestIssue571(t *testing.T) {
|
||||||
|
type Foo struct {
|
||||||
|
Float32 float32
|
||||||
|
Float64 float64
|
||||||
|
}
|
||||||
|
|
||||||
|
const closeEnough = 1e-9
|
||||||
|
|
||||||
|
foo := Foo{
|
||||||
|
Float32: 42,
|
||||||
|
Float64: 43,
|
||||||
|
}
|
||||||
|
b, err := toml.Marshal(foo)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
var foo2 Foo
|
||||||
|
err = toml.Unmarshal(b, &foo2)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
assert.InDelta(t, 42, foo2.Float32, closeEnough)
|
||||||
|
assert.InDelta(t, 43, foo2.Float64, closeEnough)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestIssue678(t *testing.T) {
|
||||||
|
type Config struct {
|
||||||
|
BigInt big.Int
|
||||||
|
}
|
||||||
|
|
||||||
|
cfg := &Config{
|
||||||
|
BigInt: *big.NewInt(123),
|
||||||
|
}
|
||||||
|
|
||||||
|
out, err := toml.Marshal(cfg)
|
||||||
|
require.NoError(t, err)
|
||||||
|
equalStringsIgnoreNewlines(t, "BigInt = '123'", string(out))
|
||||||
|
|
||||||
|
cfg2 := &Config{}
|
||||||
|
err = toml.Unmarshal(out, cfg2)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, cfg, cfg2)
|
||||||
|
}
|
||||||
|
|
||||||
func ExampleMarshal() {
|
func ExampleMarshal() {
|
||||||
type MyConfig struct {
|
type MyConfig struct {
|
||||||
Version int
|
Version int
|
||||||
@@ -822,26 +865,3 @@ func ExampleMarshal() {
|
|||||||
// Name = 'go-toml'
|
// Name = 'go-toml'
|
||||||
// Tags = ['go', 'toml']
|
// Tags = ['go', 'toml']
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestIssue571(t *testing.T) {
|
|
||||||
type Foo struct {
|
|
||||||
Float32 float32
|
|
||||||
Float64 float64
|
|
||||||
}
|
|
||||||
|
|
||||||
const closeEnough = 1e-9
|
|
||||||
|
|
||||||
foo := Foo{
|
|
||||||
Float32: 42,
|
|
||||||
Float64: 43,
|
|
||||||
}
|
|
||||||
b, err := toml.Marshal(foo)
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
var foo2 Foo
|
|
||||||
err = toml.Unmarshal(b, &foo2)
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
assert.InDelta(t, 42, foo2.Float32, closeEnough)
|
|
||||||
assert.InDelta(t, 43, foo2.Float64, closeEnough)
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -664,6 +664,36 @@ func TestUnmarshal(t *testing.T) {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
desc: "long string array into []string",
|
||||||
|
input: `A = ["0","1","2","3","4","5","6","7","8","9","10","11","12","13","14","15","16","17"]`,
|
||||||
|
gen: func() test {
|
||||||
|
type doc struct {
|
||||||
|
A []string
|
||||||
|
}
|
||||||
|
|
||||||
|
return test{
|
||||||
|
target: &doc{},
|
||||||
|
expected: &doc{A: []string{"0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15", "16", "17"}},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "long string array into []interface{}",
|
||||||
|
input: `A = ["0","1","2","3","4","5","6","7","8","9","10","11","12","13","14",
|
||||||
|
"15","16","17"]`,
|
||||||
|
gen: func() test {
|
||||||
|
type doc struct {
|
||||||
|
A []interface{}
|
||||||
|
}
|
||||||
|
|
||||||
|
return test{
|
||||||
|
target: &doc{},
|
||||||
|
expected: &doc{A: []interface{}{"0", "1", "2", "3", "4", "5", "6",
|
||||||
|
"7", "8", "9", "10", "11", "12", "13", "14", "15", "16", "17"}},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
desc: "standard table",
|
desc: "standard table",
|
||||||
input: `[A]
|
input: `[A]
|
||||||
|
|||||||
Reference in New Issue
Block a user