我想动态更改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没有与指针一起使用.那么,如何创建新类型的指针呢?

推荐答案

下面是最新的有效演示:

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().

    我们这样做是因为value := reflect.ValueOf(*u)中的value无法寻址(您可以使用fmt.Printf("%v", value.Addr())进行验证.它会panic ,并显示消息panic: reflect.Value.Addr of unaddressable value).

Go相关问答推荐

Hugo错误:没有为此项目配置现有内容目录

提供的client_secret与此帐户上任何关联的SetupIntent都不匹配

如何获取集群外go Kubernetes客户端的当前命名空间?

按位移计算结果中的差异

Caddy服务器try 打开端口80而不是8090.

在 Windows 11 上运行 go mod tidy 时的 gitlab 权限问题

如何在 Go msgraph-sdk-go 中转发消息并包括抄送和/或密送收件人?

在 Go sync.Map 中,为什么这部分实现不一致或者我误解了什么?

为什么 net/http 不遵守超过 30 秒的超时持续时间?

在 Go 中公开公共 JWK

在恒等函数中将类型 T 转换为类型 U

Protobuf.Any - 从 json.RawMessage 解组

从动态输入中提取字符串,其中部分字符串可能不存在

自定义指标未显示在 prometheus web ui 中,grafana 中也是如此

通用函数与外部包中的常见成员一起处理不同的 struct ?

如何从字符串中删除多个换行符`\n`但只保留一个?

正则表达式处理数字签名的多个条目

AWS EKS 上的 Golang REST API 部署因 CrashLoopBackOff 而失败

在 etcd 键值存储中禁用历史记录

为什么在 unsafe.Sizeof() 中取消引用 nil 指针不会导致panic ?