Golang:解析动态YAML注释

3
我想动态更改一个结构体的注释并像下面这样使用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没有使用指针。那么如何创建新类型的指针?


https://go.dev/play/p/_1UeEwrxafw - mkopriva
1个回答

3
这是一个更新后的演示,可以正常运行。
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

  2. newValue := value.Convert(newType) 替换为 newValue := reflect.NewAt(newType, unsafe.Pointer(u)).Elem()

    我们这样做是因为 valuevalue := reflect.ValueOf(*u) 中是不可寻址的(您可以使用 fmt.Printf("%v", value.Addr()) 验证它。它会抛出消息 panic: reflect.Value.Addr of unaddressable value)。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接