You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用Golang对YAML Schema进行语义校验的实现方案咨询

Hey there! Let's walk through how to solve this YAML validation problem using Go's yaml and gojsonschema libraries, including handling seq/map types and your custom validation rules.

Step 1: Define Go Structs for YAML Parsing

First, we'll map your YAML structure to Go structs. This helps go-yaml parse the file correctly and gives us a typed structure to work with:

import (
    "encoding/json"
    "fmt"
    "os"

    "gopkg.in/yaml.v3"
    "github.com/xeipuuv/gojsonschema"
)

type Config struct {
    Version         string                 `yaml:"_version"`
    ID              string                 `yaml:"id"`
    ReleaseVersion  string                 `yaml:"release-version"`
    Builds          []Build                `yaml:"builds"`
}

type Build struct {
    Name            string                 `yaml:"name"`
    Params          map[string]interface{} `yaml:"params"` // Handles arbitrary key-value pairs
}

Step 2: Parse YAML with Error Positioning

Using yaml.Decoder instead of direct Unmarshal gives us better error context (like line numbers) for syntax issues:

func parseYAML(filePath string) (*Config, error) {
    file, err := os.Open(filePath)
    if err != nil {
        return nil, fmt.Errorf("failed to open file: %w", err)
    }
    defer file.Close()

    var config Config
    decoder := yaml.NewDecoder(file)
    decoder.KnownFields(true) // Optional: Reject unknown fields to enforce schema strictly
    if err := decoder.Decode(&config); err != nil {
        // The error will include line/column info for syntax issues
        return nil, fmt.Errorf("yaml syntax error: %w", err)
    }

    return &config, nil
}

Step 3: Handle Custom Unique Validation

JSON Schema doesn't natively validate uniqueness of a field across array elements, so we'll add a custom check for builds.name before running the schema validation:

func validateUniqueBuildNames(builds []Build) error {
    seenNames := make(map[string]int)
    for idx, build := range builds {
        if pos, exists := seenNames[build.Name]; exists {
            return fmt.Errorf("build name '%s' is duplicated at index %d (first seen at index %d)", build.Name, idx, pos)
        }
        seenNames[build.Name] = idx
    }
    return nil
}

Step 4: Convert to JSON & Validate with gojsonschema

Now we'll convert our parsed Go struct to JSON (since gojsonschema works with JSON Schema) and define a schema that matches your rules:

func main() {
    // Parse YAML file
    config, err := parseYAML("your-config.yaml")
    if err != nil {
        fmt.Printf("Parsing failed: %v\n", err)
        return
    }

    // Check for duplicate build names
    if err := validateUniqueBuildNames(config.Builds); err != nil {
        fmt.Printf("Validation error: %v\n", err)
        return
    }

    // Convert Go struct to JSON bytes
    jsonData, err := json.Marshal(config)
    if err != nil {
        fmt.Printf("Failed to convert to JSON: %v\n", err)
        return
    }

    // Define your JSON Schema (matches your validation rules)
    schema := `
    {
        "$schema": "http://json-schema.org/draft-07/schema#",
        "type": "object",
        "required": ["_version", "id", "release-version", "builds"],
        "properties": {
            "_version": {"type": "string"},
            "id": {
                "type": "string",
                "pattern": "^[A-Za_\\-\\.]+$"
            },
            "release-version": {"type": "string"},
            "builds": {
                "type": "array", // Maps to your "seq" type
                "items": {
                    "type": "object", // Maps to your "map" type
                    "required": ["name"],
                    "properties": {
                        "name": {
                            "type": "string",
                            "pattern": "^[A-Za-z0-3_\\-\\.]+$"
                        },
                        "params": {
                            "type": "object",
                            "additionalProperties": true // Allows arbitrary key-value pairs
                        }
                    }
                }
            }
        }
    }
    `

    // Run schema validation
    schemaLoader := gojsonschema.NewStringLoader(schema)
    docLoader := gojsonschema.NewBytesLoader(jsonData)

    result, err := gojsonschema.Validate(schemaLoader, docLoader)
    if err != nil {
        fmt.Printf("Schema validation setup failed: %v\n", err)
        return
    }

    if result.Valid() {
        fmt.Println("✅ YAML file is valid!")
    } else {
        fmt.Println("❌ YAML file is invalid:")
        for _, err := range result.Errors() {
            // Error includes path (e.g., ".id" or ".builds[0].name") for easy debugging
            fmt.Printf("- %s\n", err)
        }
    }
}

Key Notes:

  • Seq to JSON Array: Go slices ([]Build) automatically convert to JSON arrays, which the schema validates with "type": "array".
  • Map to JSON Object: Go maps (map[string]interface{}) convert to JSON objects, handled by "type": "object" in the schema. The additionalProperties: true for params allows any key-value pairs as required.
  • Error Positioning: YAML syntax errors from yaml.Decoder include line/column numbers, while schema validation errors include paths to the invalid field (e.g., .builds[2].name).
  • Pattern Matching: The regex patterns in the JSON Schema mirror your requirements—just remember to escape backslashes in the JSON string.

内容的提问来源于stack exchange,提问作者user8557463

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.21 03:49:24