Golang中如何验证YAML文件的Schema
Golang中如何验证YAML文件的Schema 我正在尝试验证一个YAML模式,它看起来很像Kubernetes部署的YAML文件,但并不完全相同。
version: v1
kind: Extension
metadata:
name:
vendor-id:
version:
artifacts:
containers:
- name: nginx
image: nginx:1.14.2
ports:
- name: http
containerPort: 80
volumeMounts:
- mountPath: /test-ebs
name: http-volume
bundle:
- name: my-bundle
path:
web:
- name:
path:
字段 containers、bundle、web 是可选的,但其中至少一个必须存在。我在Stack Overflow上找到了这个讨论,但它看起来已经是三年半以前的了。我想知道现在是否有任何新的或更好的方法来实现这个功能。
谢谢
更多关于Golang中如何验证YAML文件的Schema的实战教程也可以访问 https://www.itying.com/category-94-b0.html
你链接的帖子在我看来是正确的:
- 将你的模型定义为一个结构体
- 将数据反序列化到该模型中
- 编写代码来检查反序列化的结果,以验证其是否有效。
更多关于Golang中如何验证YAML文件的Schema的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html
在此期间,我查看了 compose-go 和 kubeval。看起来它们是在读取 YAML 文件,将其转换为 JSON,然后再使用 JSON 验证器。
补充一下 @skillian 所说的内容,一个很好的起点可能是将你的 YAML 输入到 https://zhwt.github.io/yaml-to-go/,这会生成以下代码:
type AutoGenerated struct {
Version string `yaml:"version"`
Kind string `yaml:"kind"`
Metadata struct {
Name interface{} `yaml:"name"`
VendorID interface{} `yaml:"vendor-id"`
Version interface{} `yaml:"version"`
} `yaml:"metadata"`
Artifacts struct {
Containers []struct {
Name string `yaml:"name"`
Image string `yaml:"image"`
Ports []struct {
Name string `yaml:"name"`
ContainerPort int `yaml:"containerPort"`
} `yaml:"ports"`
VolumeMounts []struct {
MountPath string `yaml:"mountPath"`
Name string `yaml:"name"`
} `yaml:"volumeMounts"`
} `yaml:"containers"`
Bundle []struct {
Name string `yaml:"name"`
Path interface{} `yaml:"path"`
} `yaml:"bundle"`
Web []struct {
Name interface{} `yaml:"name"`
Path interface{} `yaml:"path"`
} `yaml:"web"`
} `yaml:"artifacts"`
}
以此作为起点,并硬编码 YAML:
myYaml := []byte(`version: v1
kind: Extension
metadata:
name:
vendor-id:
version:
artifacts:
containers:
- name: nginx
image: nginx:1.14.2
ports:
- name: http
containerPort: 80
volumeMounts:
- mountPath: /test-ebs
name: http-volume
bundle:
- name: my-bundle
path:
web:
- name:
path:
`)
你可以使用你 StackOverflow 链接中提到的库来解析你的 YAML,然后对其进行任何你想要的检查:
var myConfig AutoGenerated
err := yaml.Unmarshal(myYaml, &myConfig)
if err != nil {
panic(err)
}
// 进行任何你想要的检查
fmt.Println(myConfig.Artifacts.Containers[0].Name, "Is a valid config option for container name")
这显然是人为设计的,并且不太安全。但它应该能让你开始。你可以在 Go Playground 上运行这个示例,并编辑它以满足你的实际需求。
在Go中验证YAML文件的Schema,目前有几种主流方法。以下是针对你具体需求的解决方案:
1. 使用go-playground/validator(推荐)
这是当前最流行的验证库,支持结构体标签验证:
package main
import (
"fmt"
"gopkg.in/yaml.v3"
"github.com/go-playground/validator/v10"
)
type Extension struct {
Version string `yaml:"version" validate:"required,eq=v1"`
Kind string `yaml:"kind" validate:"required,eq=Extension"`
Metadata Metadata `yaml:"metadata" validate:"required"`
Artifacts *Artifacts `yaml:"artifacts,omitempty" validate:"required"`
}
type Metadata struct {
Name string `yaml:"name" validate:"required"`
VendorID string `yaml:"vendor-id" validate:"required"`
Version string `yaml:"version" validate:"required"`
}
type Artifacts struct {
Containers []Container `yaml:"containers,omitempty"`
Bundle []Bundle `yaml:"bundle,omitempty"`
Web []Web `yaml:"web,omitempty"`
}
type Container struct {
Name string `yaml:"name" validate:"required"`
Image string `yaml:"image" validate:"required"`
Ports []Port `yaml:"ports,omitempty"`
VolumeMounts []VolumeMount `yaml:"volumeMounts,omitempty"`
}
type Bundle struct {
Name string `yaml:"name" validate:"required"`
Path string `yaml:"path" validate:"required"`
}
type Web struct {
Name string `yaml:"name" validate:"required"`
Path string `yaml:"path" validate:"required"`
}
type Port struct {
Name string `yaml:"name" validate:"required"`
ContainerPort int `yaml:"containerPort" validate:"required,min=1,max=65535"`
}
type VolumeMount struct {
MountPath string `yaml:"mountPath" validate:"required"`
Name string `yaml:"name" validate:"required"`
}
// 自定义验证函数:至少一个字段存在
func validateAtLeastOne(fl validator.FieldLevel) bool {
artifacts, ok := fl.Field().Interface().(Artifacts)
if !ok {
return false
}
return len(artifacts.Containers) > 0 ||
len(artifacts.Bundle) > 0 ||
len(artifacts.Web) > 0
}
func main() {
validate := validator.New()
validate.RegisterValidation("atleastone", validateAtLeastOne)
yamlData := `
version: v1
kind: Extension
metadata:
name: my-extension
vendor-id: acme
version: 1.0.0
artifacts:
containers:
- name: nginx
image: nginx:1.14.2
ports:
- name: http
containerPort: 80
volumeMounts:
- mountPath: /test-ebs
name: http-volume
`
var ext Extension
err := yaml.Unmarshal([]byte(yamlData), &ext)
if err != nil {
fmt.Printf("YAML解析错误: %v\n", err)
return
}
err = validate.Struct(ext)
if err != nil {
fmt.Printf("验证失败: %v\n", err)
return
}
fmt.Println("YAML Schema验证通过")
}
2. 使用自定义验证方法
如果你需要更复杂的逻辑,可以实现自定义验证:
package main
import (
"errors"
"fmt"
"gopkg.in/yaml.v3"
)
func (a *Artifacts) Validate() error {
if len(a.Containers) == 0 && len(a.Bundle) == 0 && len(a.Web) == 0 {
return errors.New("artifacts中必须至少包含containers、bundle或web中的一个")
}
// 验证containers
for i, container := range a.Containers {
if container.Name == "" {
return fmt.Errorf("containers[%d].name不能为空", i)
}
if container.Image == "" {
return fmt.Errorf("containers[%d].image不能为空", i)
}
}
// 验证bundle
for i, bundle := range a.Bundle {
if bundle.Name == "" {
return fmt.Errorf("bundle[%d].name不能为空", i)
}
if bundle.Path == "" {
return fmt.Errorf("bundle[%d].path不能为空", i)
}
}
// 验证web
for i, web := range a.Web {
if web.Name == "" {
return fmt.Errorf("web[%d].name不能为空", i)
}
if web.Path == "" {
return fmt.Errorf("web[%d].path不能为空", i)
}
}
return nil
}
func (e *Extension) Validate() error {
if e.Version != "v1" {
return errors.New("version必须是v1")
}
if e.Kind != "Extension" {
return errors.New("kind必须是Extension")
}
if e.Metadata.Name == "" {
return errors.New("metadata.name不能为空")
}
if e.Metadata.VendorID == "" {
return errors.New("metadata.vendor-id不能为空")
}
if e.Metadata.Version == "" {
return errors.New("metadata.version不能为空")
}
if e.Artifacts == nil {
return errors.New("artifacts不能为空")
}
return e.Artifacts.Validate()
}
func main() {
yamlData := `
version: v1
kind: Extension
metadata:
name: test
vendor-id: vendor
version: 1.0
artifacts:
web:
- name: my-web
path: /path/to/web
`
var ext Extension
err := yaml.Unmarshal([]byte(yamlData), &ext)
if err != nil {
fmt.Printf("YAML解析错误: %v\n", err)
return
}
err = ext.Validate()
if err != nil {
fmt.Printf("验证失败: %v\n", err)
return
}
fmt.Println("验证通过")
}
3. 使用JSON Schema验证(通过YAML转换)
package main
import (
"fmt"
"github.com/santhosh-tekuri/jsonschema/v5"
"gopkg.in/yaml.v3"
)
func main() {
// JSON Schema定义
schemaJSON := `{
"$schema": "http://json-schema.org/draft-07/schema#",
"type": "object",
"required": ["version", "kind", "metadata", "artifacts"],
"properties": {
"version": {"type": "string", "const": "v1"},
"kind": {"type": "string", "const": "Extension"},
"metadata": {
"type": "object",
"required": ["name", "vendor-id", "version"],
"properties": {
"name": {"type": "string"},
"vendor-id": {"type": "string"},
"version": {"type": "string"}
}
},
"artifacts": {
"type": "object",
"oneOf": [
{"required": ["containers"]},
{"required": ["bundle"]},
{"required": ["web"]}
],
"properties": {
"containers": {
"type": "array",
"items": {
"type": "object",
"required": ["name", "image"],
"properties": {
"name": {"type": "string"},
"image": {"type": "string"}
}
}
},
"bundle": {
"type": "array",
"items": {
"type": "object",
"required": ["name", "path"],
"properties": {
"name": {"type": "string"},
"path": {"type": "string"}
}
}
},
"web": {
"type": "array",
"items": {
"type": "object",
"required": ["name", "path"],
"properties": {
"name": {"type": "string"},
"path": {"type": "string"}
}
}
}
}
}
}
}`
// 编译schema
compiler := jsonschema.NewCompiler()
if err := compiler.AddResource("schema.json", []byte(schemaJSON)); err != nil {
panic(err)
}
schema, err := compiler.Compile("schema.json")
if err != nil {
panic(err)
}
// YAML数据
yamlData := `
version: v1
kind: Extension
metadata:
name: test
vendor-id: vendor
version: 1.0
artifacts:
containers:
- name: nginx
image: nginx:1.14.2
`
// 将YAML转换为interface{}用于验证
var data interface{}
err = yaml.Unmarshal([]byte(yamlData), &data)
if err != nil {
fmt.Printf("YAML解析错误: %v\n", err)
return
}
// 验证
if err := schema.Validate(data); err != nil {
fmt.Printf("Schema验证失败: %v\n", err)
return
}
fmt.Println("YAML Schema验证通过")
}
总结
- go-playground/validator:当前最流行的选择,支持丰富的验证标签和自定义验证函数
- 自定义验证方法:提供最大的灵活性,适合复杂业务逻辑
- JSON Schema:适合已有JSON Schema或需要跨语言验证的场景
对于你的需求(至少一个字段存在),推荐使用第一种方法配合自定义验证函数,或者第二种自定义验证方法。这些方案都比三年前Stack Overflow讨论中的方法更现代和强大。

