使用 Golang 对 YAML 模式进行验证(语义检查)。

7
我们有一个工具需要读取特定结构的YAML文件。当我们获取到YAML文件后,我们需要知道以下信息:
  1. 检查YAML文件是否符合某些指南 - 语义检查
  2. 如果有语法错误,需要知道错误位置
例如,这是我们需要处理的验证示例。
 _version:  {required: true}
   id: {required: true, pattern: '/^[A-Za_\-\.]+$/'}   
   release-version: {required: true}
   type:   

   builds:
     type:seq
     sequence:
       -type:map
     mapping:
        name:{required: true, unique: true, pattern: '/^[A-Za-z0-3_\-\.]+$/'}
        params: 
          type: map
          mapping: { =: {type: any} } 

映射是一个键值对象,seq 可以有多个构建版本,类型为 any 的键值对。我们使用这个开源工具来解析 yaml:https://github.com/go-yaml/yaml。一个好的想法是将其转换为 json,然后通过验证库进行验证。在我的环境中,任何示例都将非常有帮助:https://github.com/xeipuuv/gojsonschema。但我不确定如何处理。
Type map
Type seq

为什么要使用 docker 标签? - BMitch
@BMitch - 我认为Docker和K8s有相同的任务,即验证部署的YAML模式,不是吗?所以也许我可以使用相同的想法... - user8557463
2个回答

4

这里是你可以尝试的方法。

按照预期的yaml数据的格式建立一个结构体:

type Config struct {
        Version struct {
                Required bool
        }
        ID struct {
                Required bool
                Pattern string
        }
        ReleaseVersion struct {
                Required bool
        }
        Type interface{}
        Builds struct {
                Type []interface{} `yaml:"type"`
                Sequence struct {
                        Type string
                }
                Mapping struct {
                        Name map[string]interface{}
                        Params struct {
                                Type string `yaml:"type"`
                                Mapping struct {
                                        To map[string]string `yaml:"="`
                                }
                        }
                } `yaml:"mapping"`              
        }
}

为了标识我们感兴趣的yaml数据字段名称,添加了yaml标志yaml:“somefield”

此外,许多类型未知/未确定的字段可以声明为空接口(interface{}),或者如果要“强制执行”基础形式为键值对对象,则可以将其声明为map[string]interface{}或另一个结构体。

然后,我们将yaml数据解组为该结构:

cfg := Config{}
err := yaml.Unmarshal([]byte(data), &cfg)
if err != nil {
        log.Fatalf("error: %v", err)
}

由于我们将字段建模为匿名结构体或映射,因此我们可以通过检查其等于nil来测试特定字段是否具有“键值”值。

// Mapping is a key value object
if (Mapping != nil) {
        // Mapping is a key-value object, since it's not nil.
}


// type any is and key value
// Mapping.To is declared with map[string]string type
// so if it's not nil we can say there's a map there.
if (Mapping.To != nil) {
        // Mapping.To is a map
}

在编组/解组中,映射和结构体基本可以互换使用。结构体的好处是可以预先定义字段名称,而将解组结果解组为映射时,其键名并不清晰明了。


谢谢您的回答!我有几个问题,如果您能在您的回答中添加答案,那就太好了 :) ,1.与使用jsonschema相比,像这样使用它是否更好,即转换为json并验证?2.所有这些注释(如yaml:“type” yaml:“=”等)的目的是什么,它是如何工作的?3.在golang中,interface {}map [string] interface {}是什么类型?4.您是否知道k8sdocker compose正在做什么来解决此问题,两者都具有需要验证的yaml模式吗?非常感谢!!! - user8557463
转换为JSON取决于您/您的团队。通常情况下,JSON解析更简单。除了最后一个关于Docker的问题,我已经将所有其他问题都添加到答案中了,也许最好在另一个问题中提出。 - Pandemonium
1
非常感谢!我找到了这个 https://github.com/docker/libcompose/blob/master/config/schema.go 和这个 https://github.com/docker/compose/blob/master/compose/config/config_schema_v2.0.json ,你觉得如果需要完成这个任务,为了简单和避免死路,你会选择哪种方式? - user8557463
很抱歉,但我认为这已经超出了问题的范围。 - Pandemonium
谢谢,我将关闭问题,因为已经得到了答案。我的问题是,如果你需要完成这样的任务,你会选择哪种方式呢? :) - user8557463

3
您可以使用go-yamljsonschema一起工作。参见此问题:https://github.com/santhosh-tekuri/jsonschema/issues/5 简而言之:
  1. 创建一个自定义的yaml解析器,生成兼容的输出类型,如此问题所示。
  2. 使用该自定义解析器将yaml解析为interface{}
  3. 使用jsonschema.ValidateInterface进行验证。
(一旦发布了yaml.v3,可以用配置选项替换自定义解析器)
最初我使用了被接受答案的方法,将其解析为结构并编写代码来手动验证结构是否符合规范。这很快变得非常丑陋 - 上述方法允许使用干净的单独规范,并对其进行可靠的验证。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接