如何在Go中优雅读取不同子类型的YAML(K8s CRD场景)
优化多文档YAML的GetAll泛型函数,去除冗余解码与类型转换
原代码在处理多文档YAML时,存在多次解码、冗余类型转换的问题,以下是优化后的实现,代码更简洁清晰:
优化后的代码
package main import ( "bytes" "fmt" "gopkg.in/yaml.v3" ) const yamldata = ` kind: Kind1 spec1: AAA --- kind: Kind2 spec2: BBB ` type Meta struct { Kind string `json:"kind,omitempty" yaml:"kind,omitempty"` } type Kind1 struct { Meta `json:",inline" yaml:",inline"` Spec1 string `json:"spec1,omitempty" yaml:"spec1,omitempty"` } type Kind2 struct { Meta `json:",inline" yaml:",inline"` Spec2 string `json:"spec2,omitempty" yaml:"spec2,omitempty"` } // CRDKind 定义CRD类型的接口,用于获取对应的Kind标识 type CRDKind interface { GetKind() string } func (k *Kind1) GetKind() string { return "Kind1" } func (k *Kind2) GetKind() string { return "Kind2" } func GetAll[T CRDKind](yamldata string) ([]T, error) { var result []T dec := yaml.NewDecoder(bytes.NewBufferString(yamldata)) for { var item T err := dec.Decode(&item) if err != nil { if err.Error() == "EOF" { return result, nil } return nil, err } // 校验当前文档的Kind是否匹配目标类型 if item.GetKind() == item.Meta.Kind { result = append(result, item) } } } func main() { kind1s, err := GetAll[Kind1](yamldata) if err != nil { fmt.Printf("获取Kind1实例失败: %v\n", err) return } fmt.Printf("%+v\n", kind1s[0]) kind2s, err := GetAll[Kind2](yamldata) if err != nil { fmt.Printf("获取Kind2实例失败: %v\n", err) return } fmt.Printf("%+v\n", kind2s[0]) }
优化说明
- 引入接口规范:定义
CRDKind接口,让每个CRD类型实现GetKind()方法,明确自身对应的Kind标识,避免硬编码判断。 - 简化解码流程:直接创建泛型目标类型的实例进行解码,无需先解码为
yaml.Node再二次解码,减少冗余操作。 - 清晰的类型校验:通过对比实例的
Meta.Kind和自身的GetKind()结果,判断是否为目标类型,逻辑直观。 - 完善错误处理:明确区分EOF(文档处理完成)和其他解码错误,返回合理的错误信息,避免原代码中忽略错误的问题。
- 移除冗余转换:删除原代码中不必要的类型断言和switch分支,代码结构更简洁。
内容的提问来源于stack exchange,提问作者SkyN
相关产品推荐
相关产品推荐

