Parsing 将yaml字段动态解析为Go中的一组有限结构之一

Parsing 将yaml字段动态解析为Go中的一组有限结构之一,parsing,go,yaml,marshalling,unmarshalling,Parsing,Go,Yaml,Marshalling,Unmarshalling,我有一个yaml文件,其中一个字段可以由一种可能的结构表示。为了简化代码和yaml文件,假设我有以下yaml文件: 种类:“foo” 规格: 福娃:4 种类:“条” 规格: 巴瓦尔:5 以及以下用于解析的结构: 类型规范结构{ 种类字符串'yaml:“种类”` 规范接口{}`yaml:“规范”` } 类型Foo struct{ FooVal int`yaml:“FooVal”` } 类型栏结构{ BarVal int`yaml:“BarVal”` } 我知道我可以使用map[string]

我有一个
yaml
文件,其中一个字段可以由一种可能的结构表示。为了简化代码和yaml文件,假设我有以下yaml文件:

种类:“foo”
规格:
福娃:4
种类:“条”
规格:
巴瓦尔:5
以及以下用于解析的结构:

类型规范结构{
种类字符串'yaml:“种类”`
规范接口{}`yaml:“规范”`
}
类型Foo struct{
FooVal int`yaml:“FooVal”`
}
类型栏结构{
BarVal int`yaml:“BarVal”`
}
我知道我可以使用
map[string]interface{}
作为
Spec
字段的一种类型。但实际示例更复杂,涉及更多可能的结构类型,不仅是
Foo
Bar
,这就是我不喜欢将
spec
解析到字段中的原因

我已经找到了一种解决方法:将yaml解组到中间结构中,然后检查
kind
字段,并将
map[string]interface{}
字段封送到yaml中,然后将其解组到具体类型中:

var规范
如果错误:=yaml.Unmarshal([]字节(src),&spec);呃!=零{
恐慌(错误)
}
tmp,u:=yaml.MASHAR(规范规范)
如果规格种类==“foo”{
var foo foo
yaml.Unmarshal(tmp和foo)
fmt.Printf(“foo值为%d\n”,foo.FooVal)
}
如果规格种类=“条形”{
tmp,u:=yaml.MASHAR(规范规范)
无功棒
yaml.Unmarshal(tmp和bar)
fmt.Printf(“条形值为%d\n”,bar.BarVal)
}
但它需要额外的步骤并消耗更多的内存(真正的yaml文件可能比示例中的文件大)。是否存在更优雅的方法将yaml动态解组为一组有限的结构


更新:我正在使用
github.com/go-yaml/yaml v2.1.0
yaml解析器。

您可以通过实现自定义
解组yaml
函数来实现这一点。然而,使用
v2
版本的API,您基本上可以做与现在相同的事情,只是封装得更好一点

但是,如果您切换到使用
v3
API,则会得到更好的
UnmarshalYAML
,它实际上允许您在解析后的YAML节点被处理为本机Go类型之前对其进行处理。这看起来是这样的:

主程序包
进口(
“错误”
“fmt”
“gopkg.in/yaml.v3”
)
类型规范结构{
种类字符串'yaml:“种类”`
规范接口{}`yaml:“规范”`
}
类型Foo struct{
FooVal int`yaml:“FooVal”`
}
类型栏结构{
BarVal int`yaml:“BarVal”`
}
func(s*Spec)解组字符串(值*yaml.Node)错误{
s、 Kind=“”
对于i:=0;i
我建议研究使用YAML标记而不是当前结构在YAML中建模的可能性;标签正是为此目的而设计的。而不是当前的YAML

种类:“foo”
规格:
福娃:4
你可以写

--!福
福娃:4

现在您不再需要
kind
spec
的描述结构。加载它看起来会有点不同,因为您需要一个包装根类型,您可以在其上定义
unmarshalyml
,但如果这只是更大结构的一部分,则可能是可行的。您可以访问标签
!foo
yaml.Node
标记
字段中。

要与
yaml.v2
一起使用,可以执行以下操作:

类型yamlNode结构{
解组func(接口{})错误
}
func(n*yamlNode)解组halyaml(解组hal func(接口{})错误)错误{
n、 解组
归零
}
类型规范结构{
种类字符串'yaml:“种类”`
规范接口{}`yaml:“-”`
}
func(s*Spec)解组halyaml(解组func(接口{})错误)错误{
S型规格
T型结构{
S`yaml:,内联“`
规范yamlNode`yaml:“规范”`
}
obj:=&T{}
如果错误:=unmarshal(obj);错误!=nil{
返回错误
}
*s=规格(对象s)
转辙机{
案例“foo”:
s、 规格=新(Foo)
案例“酒吧”:
s、 规格=新(巴)
违约:
恐慌(“未知种类”)
}
返回对象规范解组(s.Spec)
}


要与
yaml.v3
一起使用,可以执行以下操作:

类型规范结构{
种类字符串'yaml:“种类”`
规范接口{}`yaml:“-”`
}
func(s*Spec)解组halyaml(n*yaml.Node)错误{
S型规格
T型结构{
*S`yaml:,内联“`
Spec yaml.Node`yaml:“Spec”`
}
对象:=&T{S:(*S)(S)}
如果错误:=n.Decode(obj);错误!=nil{
返回错误
}
转辙机{
案例“foo”