goccy / go-yaml

YAML support for the Go language
MIT License
1.18k stars 133 forks source link

How to use YAMLPath to modify values? #217

Closed ivanduka closed 3 years ago

ivanduka commented 3 years ago

Hi, I am trying to change a value in YAML file, but cannot figure out how to do it with YAMLPath.

The original attempt without YAMLPath was: dynamic.yml:

http:
  services:
    websiteService:
      loadBalancer:
        servers:
        - url: http://localhost:1081
package main

import (
    "github.com/goccy/go-yaml"
    "log"
    "os"
)

func must(err error) {
    if err != nil {
        log.Fatal(err)
    }
}

func main() {
    filename := "dynamic.yml"
    perms, err := os.Stat(filename)
    must(err)

    f, err := os.ReadFile(filename)
    must(err)

    var config map[interface{}]interface{}

    err = yaml.Unmarshal(f, &config)
    must(err)

    val := config["http"].(map[string]interface{})["services"].(map[string]interface{})["websiteService"].(
    map[string]interface{})["loadBalancer"].(map[string]interface{})["servers"].([]interface{})[0].(
    map[string]interface{})["url"]

    newVal := val.(string) + "+"

    config["http"].(map[string]interface{})["services"].(map[string]interface{})["websiteService"].(
    map[string]interface{})["loadBalancer"].(map[string]interface{})["servers"].([]interface{})[0].(
    map[string]interface{})["url"] = newVal

    newConfig, err := yaml.Marshal(config)
    must(err)

    err = os.WriteFile(filename, newConfig, perms.Mode())
    must(err)
}

Now with YAMLPath it looks way more organized when reading values, but I cannot figure out how to actually modify values and save it. Is it even possible?

package main

import (
    "bytes"
    "github.com/goccy/go-yaml"
    "log"
    "os"
)

func must(err error) {
    if err != nil {
        log.Fatal(err)
    }
}

func main() {
    filename := "dynamic.yml"

    f, err := os.ReadFile(filename)
    must(err)

    path, err := yaml.PathString("$.http.services.websiteService.loadBalancer.servers[0].url")
    must(err)

    var val string
    err = path.Read(bytes.NewReader(f), &val)
    must(err)

    newVal := val + "+"

    // how to change value at path to new value and save it to the same file?
}
goccy commented 3 years ago

@ivanduka Use github.com/goccy/go-yaml/parser to get *ast.File and write new value to it .

Full example the following:

https://play.golang.org/p/utYhox4g6FE

package main

import (
    "fmt"
    "strings"

    "github.com/goccy/go-yaml"
    "github.com/goccy/go-yaml/parser"
)

func main() {
    yml := `
http:
  services:
    websiteService:
      loadBalancer:
        servers:
        - url: http://localhost:1081
`
    urlPath, err := yaml.PathString("$.http.services.websiteService.loadBalancer.servers[0].url")
    if err != nil {
        panic(err)
    }
    file, err := parser.ParseBytes([]byte(yml), 0)
    if err != nil {
        panic(err)
    }
    if err := urlPath.ReplaceWithReader(file, strings.NewReader(`https://example.com`)); err != nil {
        panic(err)
    }
    fmt.Println(file)

}
ivanduka commented 3 years ago

Thanks!