Go语言解析YAML文件时如何保留!特殊字符?
保留YAML中!开头的标签内容(Go yaml.v3库)
问题场景
现有如下YAML文件,其中包含以!开头的标签语法:
name: Amit Kumar age: !kk address: street: !123 Some Random Street city: Chennai state: TN zip: 763098 phoneNumbers: - type: home number: 0123456789 - type: work number: 0987654321
使用Go的yaml.v3库Unmarshal时,默认会剥离!开头的标签,仅保留后续内容(如person.Age变为kk,person.Address.Street变为Some Random Street),需要解析后完整保留!及标签内容。
原解析代码如下:
package main import ( "fmt" "gopkg.in/yaml.v3" "io/ioutil" ) type Address struct { Street string `yaml:"street"` City string `yaml:"city"` State string `yaml:"state"` Zip string `yaml:"zip"` } type PhoneNumber struct { Type string `yaml:"type"` Number string `yaml:"number"` } type Person struct { Name string `yaml:"name"` Age string `yaml:"age"` Address interface{} `yaml:"address"` PhoneNumbers []PhoneNumber `yaml:"phoneNumbers"` } func main() { data, err := ioutil.ReadFile("testyamltemplate.yaml") if err != nil { fmt.Println(err) return } var person Person err = yaml.Unmarshal(data, &person) if err != nil { fmt.Println(err) return } fmt.Println(person.Age) }
解决方案
YAML中!开头的内容属于标签(Tag),yaml.v3默认会分离标签和值。要保留完整内容,可通过以下两种方式实现:
方法1:使用yaml.Node捕获完整节点信息
将需要保留标签的字段类型改为yaml.Node,通过节点的Tag和Value字段拼接出完整内容:
修改结构体定义:
type Address struct { Street yaml.Node `yaml:"street"` City string `yaml:"city"` State string `yaml:"state"` Zip string `yaml:"zip"` } type Person struct { Name string `yaml:"name"` Age yaml.Node `yaml:"age"` Address Address `yaml:"address"` PhoneNumbers []PhoneNumber `yaml:"phoneNumbers"` }
在代码中拼接完整内容:
func main() { data, err := ioutil.ReadFile("testyamltemplate.yaml") if err != nil { fmt.Println(err) return } var person Person err = yaml.Unmarshal(data, &person) if err != nil { fmt.Println(err) return } // 处理Age字段 var fullAge string if person.Age.Tag != "" { fullAge = "!" + person.Age.Tag if person.Age.Value != "" { fullAge += " " + person.Age.Value } } else { fullAge = person.Age.Value } fmt.Println(fullAge) // 输出: !kk // 处理Street字段 var fullStreet string if person.Address.Street.Tag != "" { fullStreet = "!" + person.Address.Street.Tag + " " + person.Address.Street.Value } else { fullStreet = person.Address.Street.Value } fmt.Println(fullStreet) // 输出: !123 Some Random Street }
方法2:自定义类型实现yaml.Unmarshaler接口
定义一个自定义类型,实现UnmarshalYAML方法,自动将标签和值拼接为字符串:
package main import ( "fmt" "gopkg.in/yaml.v3" "io/ioutil" ) // 自定义类型,用于保留带标签的字符串 type TaggedString string // 实现yaml.Unmarshaler接口 func (ts *TaggedString) UnmarshalYAML(value *yaml.Node) error { var fullStr string if value.Tag != "" { fullStr = "!" + value.Tag if value.Value != "" { fullStr += " " + value.Value } } else { fullStr = value.Value } *ts = TaggedString(fullStr) return nil } type Address struct { Street TaggedString `yaml:"street"` City string `yaml:"city"` State string `yaml:"state"` Zip string `yaml:"zip"` } type PhoneNumber struct { Type string `yaml:"type"` Number string `yaml:"number"` } type Person struct { Name string `yaml:"name"` Age TaggedString `yaml:"age"` Address Address `yaml:"address"` PhoneNumbers []PhoneNumber `yaml:"phoneNumbers"` } func main() { data, err := ioutil.ReadFile("testyamltemplate.yaml") if err != nil { fmt.Println(err) return } var person Person err = yaml.Unmarshal(data, &person) if err != nil { fmt.Println(err) return } fmt.Println(person.Age) // 输出: !kk fmt.Println(person.Address.Street) // 输出: !123 Some Random Street }
两种方法对比
yaml.Node方式:更灵活,可获取节点的类型、位置等额外信息,适合需要精细控制节点处理的场景。- 自定义类型方式:更简洁,Unmarshal后直接得到目标字符串,无需额外拼接逻辑,适合常规场景。
内容的提问来源于stack exchange,提问作者Bindu B C
相关产品推荐
相关产品推荐

