MIkCode
MIkCode

Reputation: 2845

How to read a YAML file

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
}

Upvotes: 86

Views: 159070

Answers (6)

Aundre
Aundre

Reputation: 382

This example will show how to enforce fields to be declared in the struct schema. This is useful for catching things such as misspelled fields to assist with creating better logging when errors occur.

Note: Example code shows explicit variable types for learning purposes use := to idiomically declare/define variables in production code.

package main

import(
   "os"
   "bytes"
   "fmt"
   "log"
   "gopkg.in/yaml.v3"
)

//use `yaml:""` struct tag to parse fields name with 
//kebabcase, snakecase, and camelcase fields
type Schema struct{
   NodeName string `yaml:"node-name"`
   NodeID int `yaml:"node-id"`
}

func main(){
   var contents []byte;
   var err error;

   contents, err = os.ReadFile( "./mockfile.yaml" );

   if err != nil{
      log.Fatalf("error reading config: $%v", err);
      os.Exit(1);
   }
   //showing how to use byte io reader since file reader was
   //already shown by another user
   var reader *bytes.Reader = bytes.NewReader(contents);
   var decoder *yaml.Decoder = yaml.NewDecoder(reader);

   //default decoding action is to ignore unknown fields
   //enable known fields flag to force error
   //upon detection of an unknown field the default
   decoder.KnownFields(true);

   var configOut = Schema{};
   err = decoder.Decode(&configOut);

   if err != nil{
      log.Fatalf("error decoding file: %v", err);
      os.Exit(1)
   }

   fmt.Printf("file contents:\n%+v", configOut );

   os.Exit(0);
}
node-name: node-1
node-id: 45001
> go run main.go
file contents:
{NodeName:node-1 NodeID:45001}⏎
node-nme: node-1 #misspelled
notdefined: should not work #undefined field
> go run main.go
2024/10/02 12:03:36 error decoding file: yaml: unmarshal errors:
  line 1: field node-nme not found in type main.Schema
  line 2: field notdefined not found in type main.Schema
exit status 1

Upvotes: 0

qwertmax
qwertmax

Reputation: 3450

Your yaml file must be

hits: 5
time: 5000000

Your code should look like this:

package main

import (
    "fmt"
    "log"
    "os"

    "gopkg.in/yaml.v2"
)

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

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

    yamlFile, err := os.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. Additionally, as of Go1.16 ioutil has been deprecated

Deprecated: As of Go 1.16, the same functionality is now provided by package io or package os, and those implementations should be preferred in new code. See the specific function documentation for details.

(source)

Upvotes: 114

temple
temple

Reputation: 964

Here is the updated version for reading Yaml in Golang

file, err := os.OpenFile("config.yaml", os.O_RDONLY, 0600)

if err != nil {
    log.Fatalf("error opening/creating file: %v", err)
}
defer file.Close()

dec := yaml.NewDecoder(file)
err = dec.Decode(&Config)
if err != nil {
    err = fmt.Errorf("error loading config file %v!", err)
}else{
    pp.Print(Config)
}

Upvotes: 0

Sameera Damith
Sameera Damith

Reputation: 613

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)
    }

Upvotes: 2

kubanczyk
kubanczyk

Reputation: 5941

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"`
}

Upvotes: 29

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

Upvotes: 2

Related Questions