Reputation: 2845
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
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
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
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
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
Reputation: 5941
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 .
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.
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
Reputation: 61
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