goyamlmarshallingunmarshalling

Preserving Single Quotes in YAML File When Editing


I want to edit the value of certain keys in a YAML file while keeping the rest unchanged. I have written a snippet to insert some values for these keys, but the resulting new file does not maintain single quotes ('). How can I avoid this situation?

my code:

func updateVariables(nameValue, nameCluster string) error {
    
    yamlFile, err := os.ReadFile("path")
    if err != nil {
        return fmt.Errorf("Error reading YAML file: %v", err)
    }

    var config PipelineConfig

    err = yaml.Unmarshal(yamlFile, &config)
    if err != nil {
        return fmt.Errorf("Error parsing YAML file: %v", err)
    }

    for i := range config.Variables {
        switch config.Variables[i].Name {
        case "app_name":
            config.Variables[i].Value = nameValue
        case "cluster_name":
            config.Variables[i].Value = nameCluster

        }
    }

    modifiedYAML, err := yaml.Marshal(&config,)
    if err != nil {
        return fmt.Errorf("Error converting structure to YAML: %v", err)
    }

    err = os.WriteFile("path", modifiedYAML, 0644)
    if err != nil {
        return fmt.Errorf("Error writing modified YAML file: %v", err)
    }
    fmt.Println("File YAML modified.")
    return nil
}

my struct:

type PipelineConfig struct {
    Trigger   string `yaml:"trigger"`
    Variables []struct {
        Name  string `yaml:"name"`
        Value string `yaml:"value"`
    } `yaml:"variables"`
    
    Stages []struct {
        Template   string `yaml:"template"`
        Parameters struct {
            AppName       string `yaml:"app_name"`
            AppRepoBranch string `yaml:"app_repo_branch"`
            LocationEnv   string `yaml:"location_env"`
            ComponentName string `yaml:"component_name"`
            ClusterName   string `yaml:"cluster_name"`
        } `yaml:"parameters"`
    } `yaml:"stages"`
}

file yaml before edit

trigger: none

variables:
  - name: app_name
    value: '<name>'
  - name: cluster_name
    value: '<cluster>'
  - name: component_name
    value: '<component>'
  - name: location_env
    value: 'dev'


stages:
  - template: 'tem'
    parameters:
      app_name: '${{ variables.app_name }}'
      app_repo_branch: 'dev'
      location_env: '${{ variables.location_env }}'
      component_name: '${{ variables.component_name }}'
      cluster_name: '${{ variables.cluster_name }}'

file yaml after edit

trigger: none
variables:
    - name: app_name
      value: test
    - name: cluster_name
      value: test
    - name: component_name
      value: <component>
    - name: location_env
      value: dev

stages:
    - template: tem
      parameters:
        app_name: ${{ variables.app_name }}
        app_repo_branch: develop
        location_env: ${{ variables.location_env }}
        component_name: ${{ variables.component_name }}
        cluster_name: ${{ variables.cluster_name }}

As you can see, the single quotes disappear. Any suggestions?


Solution

  • yaml.Unmarshal function unmarshal yaml value to a custom struct without metadata (style, kind, etc.). yaml.Marshal function under processing on a custom struct set metadata values as default. To get access metadata's fields, need to use yaml.Node.

    In your case Value field has yaml.Style equal yaml.SingleQuotedStyle

    enter image description here

    To get access to it (don't lose after unmarshalling), change Value field type to yaml.Node.

    
    Variables []struct {
        Name  string    `yaml:"name"`
        Value yaml.Node `yaml:"value"`
    } `yaml:"variables"`
    
    for i := range config.Variables {
        switch config.Variables[i].Name.Value {
        case "app_name":
            config.Variables[i].Value.Value = nameValue
        case "cluster_name":
            config.Variables[i].Value.Value = nameCluster
        }
    }
    

    PLAYGROUND