使用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. TheadditionalProperties: trueforparamsallows any key-value pairs as required. - Error Positioning: YAML syntax errors from
yaml.Decoderinclude 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

