golang:解组动态YAML通告

xqkwcwgp  于 2023-03-21  发布在  Go
关注(0)|答案(1)|浏览(144)

我想动态更改struct的注解,并使用yaml.Unmarshal,如下所示:

package main

import (
    "fmt"
    "reflect"

    "gopkg.in/yaml.v3"
)

type User struct {
    Name string `yaml:"dummy"`
}

func (u *User) UnmarshalYAML(node *yaml.Node) error {
    value := reflect.ValueOf(*u)
    t := value.Type()
    fields := make([]reflect.StructField, 0)

    for i := 0; i < t.NumField(); i++ {
        fields = append(fields, t.Field(i))

        if t.Field(i).Name == "Name" {
            fields[i].Tag = `yaml:"name"` // Dynamic annotation
        }
    }

    newType := reflect.StructOf(fields)
    newValue := value.Convert(newType)

    err := node.Decode(newValue.Interface()) // Cause error because it's not pointer
    return err
}

var dat string = `name: abc`

func main() {
    out := User{}
    yaml.Unmarshal([]byte(dat), &out)
    fmt.Printf("%+v\n", out)
}

它会导致panic: reflect: reflect.Value.Set using unaddressable value [recovered]这样的错误,我想这是因为node.Decode没有和指针一起使用。那么如何创建新类型的指针呢?

3wabscal

3wabscal1#

下面是更新后的演示:

package main

import (
    "fmt"
    "reflect"
    "unsafe"

    "gopkg.in/yaml.v3"
)

type User struct {
    Name string `yaml:"dummy"`
}

func (u *User) UnmarshalYAML(node *yaml.Node) error {
    t := reflect.TypeOf(*u)
    fields := make([]reflect.StructField, 0)

    for i := 0; i < t.NumField(); i++ {
        fields = append(fields, t.Field(i))

        if t.Field(i).Name == "Name" {
            fields[i].Tag = `yaml:"name"` // Dynamic annotation
        }
    }

    newType := reflect.StructOf(fields)
    newValue := reflect.NewAt(newType, unsafe.Pointer(u)).Elem()

    err := node.Decode(newValue.Addr().Interface())
    return err
}

var dat string = `name: abc`

func main() {
    out := User{}
    yaml.Unmarshal([]byte(dat), &out)
    fmt.Printf("%+v\n", out)
}

两个关键变化:
1.将newValue.Interface()替换为newValue.Addr().Interface()。(请参见此示例:https://pkg.go.dev/reflect#example-StructOf)
1.将newValue := value.Convert(newType)替换为newValue := reflect.NewAt(newType, unsafe.Pointer(u)).Elem()
我们这样做是因为value := reflect.ValueOf(*u)中的value是不可寻址的(您可以使用fmt.Printf("%v", value.Addr())来验证它。它会发出消息panic: reflect.Value.Addr of unaddressable value,从而引起死机)。

相关问题