英文:
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 (
"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)
}
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)
}
两个关键更改:
-
将
newValue.Interface()
替换为newValue.Addr().Interface()
。(参考示例:https://pkg.go.dev/reflect#example-StructOf) -
将
newValue := value.Convert(newType)
替换为newValue := reflect.NewAt(newType, unsafe.Pointer(u)).Elem()
。我们这样做是因为
value
在value := 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 (
"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)
}
Two key changes:
-
Replace
newValue.Interface()
withnewValue.Addr().Interface()
. (See this example: https://pkg.go.dev/reflect#example-StructOf) -
Replace
newValue := value.Convert(newType)
withnewValue := reflect.NewAt(newType, unsafe.Pointer(u)).Elem()
.We do this because
value
invalue := reflect.ValueOf(*u)
is unaddressable (You can verify it withfmt.Printf("%v", value.Addr())
. It panics with messagepanic: reflect.Value.Addr of unaddressable value
).
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论