admin管理员组

文章数量:1123649

I'm trying to parse YAML in Go where I have dynamic conditions. Each condition corresponds to a specific struct that implements a common interface. Here's a simplified example of my YAML configuration:

- id: MULTISTAGE01
  description: Do not install or keep any compiled programs
  stage: final
  conditions:
    - command.RUN:
        cmd: ".*(gcc|nodejs|clang).*"

- id: AVOID01
  description: Do not use ADD command
  conditions:
    - command.ADD:
      source: "*"

- id: SECRET01
  description: Do not expose secrets
  conditions:
    - operator.OR:
      - command.COPY:
        files:
          - "*.env"
      - command.ENV:
        key: "API.*KEY" 

Each conditions entry could represent a FROM, EXPOSE, OR, ADD, or other type of condition. Each of these is implemented as a separate struct in my Go code, and they all implement a common Condition interface, which has a Validate() method.

Here’s an example of the structs and the interface:

type Condition interface {
    Validate() error
}

type FROM struct {
    BaseCondition
    Tag   *string `yaml:"tag,omitempty"`
    Image *string `yaml:"tag,omitempty"`
}

// Validate implements Condition.
func (from FROM) Validate(stages *[]models.Stage) bool {
    result := true
    for _, stage := range *stages {
        if from.Image != nil {
            re, err := regexp.Compile(*from.Image)
            if err != nil {
                panic(err)
            }
            result = result && re.Match([]byte(stage.Image.Name))
        }

        if from.Tag != nil {
            re, err := regexp.Compile(*from.Tag)
            if err != nil {
                panic(err)
            }
            result = result && re.Match([]byte(stage.Image.Name))
        }

    }

    return result
}

My goal is to unmarshal this YAML into a []Rule struct, where each Rule contains a slice of Condition:

type Rule struct {
    Id          string       `yaml:"id"`
    Description string       `yaml:"description"`
    Conditions  []Condition  `yaml:"conditions"`
}

The Problem

I'm stuck on how to write the unmarshalling logic for the Conditions field because I need to dynamically determine which struct (e.g., FORM, EXPOSE, etc.) to use based on the key (command.FROM, stage.EXPOSE, etc.).

What have i tried

I have tried to use golang inheritage but it didn't work:

type FROM struct {
    BaseCondition
    Tag   *string `yaml:"tag,omitempty"`
    Image *string `yaml:"tag,omitempty"`
}

// Validate implements Condition.
func (from FROM) Validate(stages *[]models.Stage) bool {
    result := true
    for _, stage := range *stages {
        if from.Image != nil {
            re, err := regexp.Compile(*from.Image)
            if err != nil {
                panic(err)
            }
            result = result && re.Match([]byte(stage.Image.Name))
        }

        if from.Tag != nil {
            re, err := regexp.Compile(*from.Tag)
            if err != nil {
                panic(err)
            }
            result = result && re.Match([]byte(stage.Image.Name))
        }

    }

    return result
}

How can I implement custom unmarshalling for this YAML structure in Go?

Edit

I tried to implement the UnmarshalYAML method but i keep donc really know what to do next since the types doesn't match

type BaseCondition struct {
    Type string `yaml:"-"`
}

type RawCondition map[string]interface{}

func (c *RawCondition) UnmarshalYAML(unmarshal func(interface{}) error) error {
    var raw map[string]interface{}
    if err := unmarshal(&raw); err != nil {
        return err
    }

    for k, v := range raw {
        switch k {
        case "command.FROM":
            var cond FROM
            cond.Type = k
            condData, _ := yaml.Marshal(v)
            yaml.Unmarshal(condData, &cond)
            *c = cond
        }
    }
    return nil
}

本文标签: How to Dynamically Unmarshal YAML into Structs Implementing a Common Interface in GoStack Overflow