knadh / koanf

Simple, extremely lightweight, extensible, configuration management library for Go. Support for JSON, TOML, YAML, env, command line, file, S3 etc. Alternative to viper.
MIT License
2.78k stars 152 forks source link

How to load slice of maps from yaml #318

Closed ehmo closed 1 month ago

ehmo commented 1 month ago

I apologize for posting this but banging my head for way too long with this so maybe you can help me to solve it faster.

I have a yaml file containing something like

one:
- "two": "three"
- "four": "five"

that I am trying to load in go. I can get it raw as k.Get("one") and but then running into conversion issues.

I assume there is an easy way but my brain might be not working properly at this point.

Appreciate any help.

knadh commented 1 month ago

If you want to unmarshal this into a struct, something like this:

s := struct {
   One map[string]string `koanf:"one"`
}{}

k.Unmarshal(....)

If you want to just access the map, k.StringMap("one") which returns a map[string]string

knadh commented 1 month ago

Please refer to the documentation to see the various shortcut key access functions: https://pkg.go.dev/github.com/knadh/koanf#pkg-index

ehmo commented 1 month ago

I did use k.StringMap("one") but it returns map[]. That's one of the first things I tried.

knadh commented 1 month ago

Arghhh, YAML. I misread it as a {key: value, key: value} map. Since this is arbitrarily nested and typed, no shortcut functions can give you direct access. You have to do a Get() and handle your arbitrary types manually.

Here's pseudocode that works for your config (I've converted it to JSON for clarity).

package main

import (
    "fmt"

    "github.com/knadh/koanf/parsers/json"
    "github.com/knadh/koanf/providers/rawbytes"
    "github.com/knadh/koanf/v2"
)

func main() {
    k := koanf.New(".")

    j := `
{
  "one": [
    {
      "two": "three"
    },
    {
      "four": "five"
    }
  ]
}
    `

    if err := k.Load(rawbytes.Provider([]byte(j)), json.Parser()); err != nil {
        panic(fmt.Errorf("koanf load: %v", err))
    }

    one := k.Get("one").([]interface{})

    for _, item := range one {
        mp, ok := item.(map[string]interface{})
        if !ok {
            panic("not map[string]interface{}")
        }

        for key, value := range mp {
            fmt.Printf("%s -> %s\n", key, value)
        }
    }
}
$ go run main.go                                                                                                                                                                      
two -> three
four -> five
ehmo commented 1 month ago

That explains it. I thought I was losing my mind!

Thank you very much for your help.