77

I have an issue with reading a YAML file. I think it's something in the file structure but I can't figure out what.

YAML file:

conf:
  hits:5
  time:5000000

code:

type conf struct {
    hits int64 `yaml:"hits"`
    time int64 `yaml:"time"`
}


func (c *conf) getConf() *conf {

    yamlFile, err := ioutil.ReadFile("conf.yaml")
    if err != nil {
        log.Printf("yamlFile.Get err   #%v ", err)
    }
    err = yaml.Unmarshal(yamlFile, c)
    if err != nil {
        log.Fatalf("Unmarshal: %v", err)
    }

    return c
}
Jonathan Hall
  • 75,165
  • 16
  • 143
  • 189
MIkCode
  • 2,655
  • 5
  • 28
  • 46
  • 1
    What is the error you're seeing? – Amit Kumar Gupta Jun 19 '15 at 21:39
  • @AmitKumarGupta i dont get any error , just empty struct – MIkCode Jun 19 '15 at 21:40
  • Do you think that might be relevant to add to your question? Also, can you paste the full code that reproduces the problem? `package main`, what files you're importing, a `main` function, etc. so that it's easy for people trying to help you to copy-paste the code and run it locally. Think about adding information and code that makes it easy for people to help you, because you want people to help you. – Amit Kumar Gupta Jun 19 '15 at 21:42
  • 4
    As mentioned ad-nausem, anything setting fields via reflection (JSON, XML, YAML unmarshalling, etc) can only work on exported fields. – Dave C Jun 19 '15 at 23:51

4 Answers4

105

your yaml file must be

hits: 5
time: 5000000

your code should look like this:

package main

import (
    "fmt"
    "gopkg.in/yaml.v2"
    "io/ioutil"
    "log"
)

type conf struct {
    Hits int64 `yaml:"hits"`
    Time int64 `yaml:"time"`
}

func (c *conf) getConf() *conf {

    yamlFile, err := ioutil.ReadFile("conf.yaml")
    if err != nil {
        log.Printf("yamlFile.Get err   #%v ", err)
    }
    err = yaml.Unmarshal(yamlFile, c)
    if err != nil {
        log.Fatalf("Unmarshal: %v", err)
    }

    return c
}

func main() {
    var c conf
    c.getConf()

    fmt.Println(c)
}

the main error was capital letter for your struct.

030
  • 10,842
  • 12
  • 78
  • 123
qwertmax
  • 3,120
  • 2
  • 29
  • 42
  • 1
    The spec on exported identifiers: http://golang.org/doc/go_spec.html#Exported_identifiers – elithrar Jun 20 '15 at 05:42
  • 2
    This doesn't work for me. I need to change the line `err = yaml.Unmarshal(yamlFile, c)` to `err = yaml.Unmarshal(yamlFile, &c)` i.e. pass a pointer not a value. I have no clue why though. – förschter Mar 03 '20 at 11:19
  • It is tied down to code via type conf struct. Won't be reusable, if config file is changed. Is there anything that is dynamic, without having to define a struct in the code? – Mopparthy Ravindranath May 27 '20 at 08:08
  • 2
    @joelfischerr you're absolutely right, I had to make the same change to work it fine. – fabulias Aug 30 '20 at 22:50
  • @MopparthyRavindranath I think you need to use interfaces to do that, but why a config file will change several times? – fabulias Aug 30 '20 at 22:52
  • @MopparthyRavindranath I think you want [something like a map of strings](https://stackoverflow.com/a/26290943/122727), no? – kubanczyk Jan 12 '21 at 07:59
  • @fabulias and förschter If you are getting error on unmarshall when you dont use a reference of a pointer, the reason for this is your pointer is undefined or nil. Unmarshall requires the address in 2 cases: If its a value type or if its a nil pointer type – coder Sep 24 '22 at 09:56
  • @förschter same in your case too, I am guessing – coder Sep 24 '22 at 09:57
22

Example

Using an upgraded version 3 of yaml package.

An example conf.yaml file:

conf:
  hits: 5
  time: 5000000
  camelCase: sometext

The main.go file:

package main

import (
    "fmt"
    "io/ioutil"
    "log"

    "gopkg.in/yaml.v3"
)

type myData struct {
    Conf struct {
        Hits      int64
        Time      int64
        CamelCase string `yaml:"camelCase"`
    }
}

func readConf(filename string) (*myData, error) {
    buf, err := ioutil.ReadFile(filename)
    if err != nil {
        return nil, err
    }

    c := &myData{}
    err = yaml.Unmarshal(buf, c)
    if err != nil {
        return nil, fmt.Errorf("in file %q: %w", filename, err)
    }

    return c, err
}

func main() {
    c, err := readConf("conf.yaml")
    if err != nil {
        log.Fatal(err)
    }

    fmt.Printf("%#v", c)
}

Running instructions (in case it's the first time you step out of stdlib):

cat conf.yaml
go mod init example.com/whatever
go get gopkg.in/yaml.v3
cat go.sum
go run .

About The yaml:"field"

The tags (like yaml:"field") are optional for all-lowercase yaml key identifiers. For demonstration the above example parses an extra camel case identifier which does require such a tag.

Corner Case: JSON+YAML

Confusingly, the useful lowercasing behavior of yaml package is not seen in the standard json package. Do you have a data structure which is sometimes encoded to JSON and sometimes to YAML? If so, consider specifying both JSON tags and YAML tags on literally every field. Verbose, but reduces mistakes. Example below.

type myData struct {
    Conf conf `yaml:"conf" json:"conf"`
}

type conf struct {
    Hits      int64  `yaml:"hits" json:"hits"`
    Time      int64  `yaml:"time" json:"time"`
    CamelCase string `yaml:"camelCase" json:"camelCase"`
}
kubanczyk
  • 5,184
  • 1
  • 41
  • 52
1
package main

import (
    "gopkg.in/yaml.v2"
    "io/ioutil"
    "log"
)

type someConf struct {
    AccessKeyID     string `yaml:"access_key_id"`
   //...
}

func getConf(file string, cnf interface{}) error {
   yamlFile, err := ioutil.ReadFile(file)
   if err == nil {
      err = yaml.Unmarshal(yamlFile, cnf)
   }
   return err
}

func main() {
    cfg := &awsConf{}
    if err := getConf("conf.yml", cfg); err != nil {
        log.Panicln(err)
    }
 }

shortest getConf ever

0

Here, You can read a YAML file without having a predefined struct. Please substitute "config.yaml" with the desired file name. Add "fmt," "io/ioutil," and "gopkg.in/yaml.v2" to the import list.

    package main

    import (
        "fmt"
        "io/ioutil"

        "gopkg.in/yaml.v2"
    )

    func main() {
        obj := make(map[string]interface{})
    
        yamlFile, err := ioutil.ReadFile("config.yaml")
        if err != nil {
          fmt.Printf("yamlFile.Get err #%v ", err)
        }
        err = yaml.Unmarshal(yamlFile, obj)
        if err != nil {
          fmt.Printf("Unmarshal: %v", err)
        }

        fmt.Println(obj)
    }
Sameera Damith
  • 451
  • 7
  • 9