golang:解析动态的YAML注释

huangapple go评论87阅读模式
英文:

golang: Unmarshal dynamic YAML annonation

问题

我想动态更改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"` // 动态注释
        }
    }

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

    err := node.Decode(newValue.Interface()) // 因为不是指针,所以会导致错误
    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没有使用指针的原因。那么如何创建新类型的指针呢?

英文:

I want to dynamically change the annotation of a struct and use yaml.Unmarshal like below:

package main

import (
    &quot;fmt&quot;
    &quot;reflect&quot;

    &quot;gopkg.in/yaml.v3&quot;
)

type User struct {
    Name string `yaml:&quot;dummy&quot;`
}

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

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

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

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

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

var dat string = `name: abc`

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

It causes errors like panic: reflect: reflect.Value.Set using unaddressable value [recovered] and I think it's because node.Decode is not used with a pointer. So how to create the pointer of the new type?

答案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"` // 动态注释
		}
	}

	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 的错误信息)。

英文:

Here goes the updated demo that works:

package main

import (
	&quot;fmt&quot;
	&quot;reflect&quot;
	&quot;unsafe&quot;

	&quot;gopkg.in/yaml.v3&quot;
)

type User struct {
	Name string `yaml:&quot;dummy&quot;`
}

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

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

		if t.Field(i).Name == &quot;Name&quot; {
			fields[i].Tag = `yaml:&quot;name&quot;` // 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), &amp;out)
	fmt.Printf(&quot;%+v\n&quot;, out)
}

Two key changes:

  1. Replace newValue.Interface() with newValue.Addr().Interface(). (See this example: https://pkg.go.dev/reflect#example-StructOf)

  2. Replace newValue := value.Convert(newType) with newValue := reflect.NewAt(newType, unsafe.Pointer(u)).Elem().

    We do this because value in value := reflect.ValueOf(*u) is unaddressable (You can verify it with fmt.Printf(&quot;%v&quot;, value.Addr()). It panics with message panic: reflect.Value.Addr of unaddressable value).

huangapple
  • 本文由 发表于 2023年3月20日 05:03:22
  • 转载请务必保留本文链接:https://go.coder-hub.com/75784912.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定