documentation about he (Un)?Marshalers

This commit is contained in:
Daisuke Maki 2019-10-26 18:39:15 +09:00
parent 01e45ca83c
commit 40ece17985
3 changed files with 111 additions and 0 deletions

View file

@ -63,6 +63,15 @@ if err := yaml.Unmarshal([]byte(yml), &v); err != nil {
}
```
For custom marshal/unmarshaling, implement one of Bytes or Interface Marshaler/Unmarshaler. The difference is that while BytesMarshaler/BytesUnmarshaler behave like `encoding.json`, InterfaceMarshaler/InterfaceUnmarshaler behave like `gopkg.in/yaml.v2`.
Semantically both are the same, but they differ in performance. Because indentation matter in YAML, you cannot simply accept a valid YAML fragment from a Marshaler, and expect it to work when it is attached to the parent container's serialized form. Therefore when we receive use the BytesMarshaler, which returns []byte, we must decode it once to figure out how to make it work in the given context. If you use the InterfaceMarshaler, we can skip the decoding.
If you are repeatedly marshaling complex objects, the latter is always better
performance wise. But if you are, for example, just providing a choice between
a config file format that is read only once, the former is probably easier to
code.
## 2. Reference elements in declared in another file
`testdata` directory includes `anchor.yml` file

View file

@ -279,6 +279,13 @@ func TestDecoder(t *testing.T) {
"a: &a [1, 2]\nb: *a\n",
struct{ B []int }{[]int{1, 2}},
},
{
"tags:\n- hello-world\na: foo",
struct {
Tags []string
A string
}{Tags: []string{"hello-world"}, A: "foo"},
},
}
for _, test := range tests {
buf := bytes.NewBufferString(test.source)

View file

@ -4,6 +4,7 @@ import (
"bytes"
"fmt"
"math"
"strconv"
"testing"
"github.com/goccy/go-yaml"
@ -406,3 +407,97 @@ func Example_Marshal_ImplicitAnchorAlias() {
// c: *a
// d: *b
}
type tMarshal []string
func (t *tMarshal) MarshalYAML() ([]byte, error) {
var buf bytes.Buffer
buf.WriteString("tags:\n")
for i, v := range *t {
if i == 0 {
fmt.Fprintf(&buf, "- %s\n", v)
} else {
fmt.Fprintf(&buf, " %s\n", v)
}
}
return buf.Bytes(), nil
}
func Test_Marshaler(t *testing.T) {
const expected = `- hello-world
`
// sanity check
var l []string
if err := yaml.Unmarshal([]byte(expected), &l); err != nil {
t.Fatalf("failed to parse string: %s", err)
}
buf, err := yaml.Marshal(tMarshal{"hello-world"})
if err != nil {
t.Fatalf("failed to marshal: %s", err)
}
if string(buf) != expected {
t.Fatalf("expected '%s', got '%s'", expected, buf)
}
t.Logf("%s", buf)
}
type SlowMarshaler struct {
A string
B int
}
type FastMarshaler struct {
A string
B int
}
func (v SlowMarshaler) MarshalYAML() ([]byte, error) {
var buf bytes.Buffer
buf.WriteString("tags:\n")
buf.WriteString("- slow-marshaler\n")
buf.WriteString("a: " + v.A + "\n")
buf.WriteString("b: " + strconv.FormatInt(int64(v.B), 10) + "\n")
return buf.Bytes(), nil
}
func (v FastMarshaler) MarshalYAML() (interface{}, error) {
return yaml.MapSlice{
{"tags", []string{"fast-marshaler"}},
{"a", v.A},
{"b", v.B},
}, nil
}
func Example_MarshalYAML() {
var slow SlowMarshaler
slow.A = "Hello slow poke"
slow.B = 100
buf, err := yaml.Marshal(slow)
if err != nil {
panic(err.Error())
}
fmt.Println(string(buf))
var fast FastMarshaler
fast.A = "Hello speed demon"
fast.B = 100
buf, err = yaml.Marshal(fast)
if err != nil {
panic(err.Error())
}
fmt.Println(string(buf))
// OUTPUT:
// tags:
// - slow-marshaler
// a: Hello slow poke
// b: 100
//
// tags:
// - fast-marshaler
// a: Hello speed demon
// b: 100
}