Golang中UnmarshalYAML未被调用的问题如何解决

Golang中UnmarshalYAML未被调用的问题如何解决 序列化一个包含 Wrapper 结构体字段的结构体 C 时,该 Wrapper 实现了 MarshalYAML 方法,但该方法未被调用。 这是一个错误,还是我哪里做错了?

package ci

import (
	"errors"
	"fmt"
	"testing"

	"github.com/stretchr/testify/require"
	"gopkg.in/yaml.v3"
)

type Interface interface {
	DoUnmarshal(v *yaml.Node) error
}

type Impl struct {
	A string `yaml:"aa"`
}

func (s *Impl) DoUnmarshal(v *yaml.Node) error {
	return v.Decode(&s)
}

type MyType struct {
	Value  bool    `yaml:"v"`
	Custom Wrapper `yaml:"custom"`
}

type Wrapper struct {
	inst Interface
}

var called bool

func (w *Wrapper) MarshalYAML() (interface{}, error) {
	fmt.Printf("Marshal wrapper.")
	called = true
	return w.inst, nil
}

func (w *Wrapper) UnmarshalYAML(v *yaml.Node) error {
	fmt.Printf("Unmarshal wrapper.")
	return w.inst.DoUnmarshal(v)
}

func TestUnmarshal(t *testing.T) {

	c := MyType{
		Value:  true,
		Custom: Wrapper{inst: &Impl{A: "asdf"}}}

	b, e := yaml.Marshal(&c)

	require.NoError(t, e)
	t.Logf("\n%v", string(b))
	require.True(t, called, "MarshalYAML is not called.")
}

更多关于Golang中UnmarshalYAML未被调用的问题如何解决的实战教程也可以访问 https://www.itying.com/category-94-b0.html

3 回复
type MyType struct {
	Value  bool     `yaml:"v"`
	Custom *Wrapper `yaml:"custom"`
}
func TestUnmarshal(t *testing.T) {

	c := MyType{
		Value:  true,
		Custom: &Wrapper{inst: &Impl{A: "asdf"}},
	}

更多关于Golang中UnmarshalYAML未被调用的问题如何解决的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


补充一下@peakedshout提到的内容:你满足的是指针接收器的接口,而不是值接收器的接口。看看这个StackOverflow上的问题:

Anders Sjöqvist

Custom MarshalJSON() never gets called in Go

标签 json, go, marshalling

提问者 Anders Sjöqvist08:26PM - 27 Jan 14 UTC

这个问题是关于 MarshalJSON 的,但同样的道理也适用。

在Go的yaml.v3包中,MarshalYAML()方法需要满足特定的签名要求才能被正确调用。根据你的代码,问题在于MarshalYAML()方法应该返回(interface{}, error),但yaml.v3包实际上期望的是MarshalYAML() (*yaml.Node, error)签名。

以下是修正后的代码示例:

package ci

import (
	"fmt"
	"testing"

	"github.com/stretchr/testify/require"
	"gopkg.in/yaml.v3"
)

type Interface interface {
	DoUnmarshal(v *yaml.Node) error
}

type Impl struct {
	A string `yaml:"aa"`
}

func (s *Impl) DoUnmarshal(v *yaml.Node) error {
	return v.Decode(&s)
}

type MyType struct {
	Value  bool    `yaml:"v"`
	Custom Wrapper `yaml:"custom"`
}

type Wrapper struct {
	inst Interface
}

var called bool

func (w *Wrapper) MarshalYAML() (interface{}, error) {
	fmt.Printf("Marshal wrapper.")
	called = true
	// 对于yaml.v3,需要返回*yaml.Node
	node := &yaml.Node{}
	err := node.Encode(w.inst)
	return node, err
}

func (w *Wrapper) UnmarshalYAML(v *yaml.Node) error {
	fmt.Printf("Unmarshal wrapper.")
	return w.inst.DoUnmarshal(v)
}

func TestUnmarshal(t *testing.T) {
	c := MyType{
		Value:  true,
		Custom: Wrapper{inst: &Impl{A: "asdf"}}}

	b, e := yaml.Marshal(&c)

	require.NoError(t, e)
	t.Logf("\n%v", string(b))
	require.True(t, called, "MarshalYAML is not called.")
}

如果你使用的是yaml.v2版本,MarshalYAML()方法签名可以是(interface{}, error)。但对于yaml.v3,需要调整为返回(*yaml.Node, error)

func (w *Wrapper) MarshalYAML() (*yaml.Node, error) {
	fmt.Printf("Marshal wrapper.")
	called = true
	node := &yaml.Node{}
	err := node.Encode(w.inst)
	return node, err
}

另外,确保UnmarshalYAML方法在反序列化时被正确调用,需要验证yaml数据是否包含custom字段。测试示例如下:

func TestUnmarshalYAML(t *testing.T) {
	yamlData := `v: true
custom:
  aa: "test"`
	
	var result MyType
	result.Custom = Wrapper{inst: &Impl{}}
	
	err := yaml.Unmarshal([]byte(yamlData), &result)
	require.NoError(t, err)
	require.Equal(t, "test", result.Custom.inst.(*Impl).A)
}
回到顶部