Trouble unmarshaling YAML structure - go

I have what I think is a very straightforward YAML structure I'm attempting to write to and read from a file.
appName:
version: 1.2.3.4
md5_checksum: 987654321
And I'm really struggling to understand nested structs and how they relate to yaml marshaling.
At this point I have the following:
type Application struct {
Name string `yaml:"application"`
Version string `yaml:"version"`
Checksum int `yaml:"md5_checksum"`
}
yamlData := Application{"MyProgram", "1.2.3.4", 34235234123}
y, err := yaml.Marshal(yamlData)
if err != nil {
log.Fatal("Yaml marshal failed")
}
err = ioutil.WriteFile("applications.yaml", y, 0644)
if err != nil {
log.Fatal("File write failed")
}
This gets me close, but the file reads as follows:
application: MyProgram
version: 1.2.3.4
md5_checksum: 34235234123
That looks nice, but this file will be populated by many applications. I've tried a few types of nested structs, but I get hung up on how to supply values to them when finally building them.

You can define Application struct as
type AppVersion struct {
Version string `yaml:"version"`
Checksum int `yaml:"md5_checksum"`
}
type Application struct {
Name AppVersion `yaml:"appName"`
}
Then call AppVersion as
yamlData := Application{Name: AppVersion{Version: "1.2.3.4", Checksum: 34235234123}}

I've solved my own problem with help from KibGzr.
No structs necessary just for the marshaling steps.
yamlData := map[string]map[string]string{
appName: map[string]string{"version": appVersion, "md5_checksum": checksumString},
}
I've created a nested map rather than a struct. This allows me just initialize the map when I've collected all my values rather than trying to work around struct field names. My output is now:
appName:
md5_checksum: checksumString
version: appVersion
Thanks for all the help!

Related

How to make an object of a class and have getters for struct?

I have recently started working with golang so having difficulties understanding on how to achieve same thing which I am able to do it easily in Java or C#. I am trying to make an object of configmanager class and when the first time configmanager class is called, it should initialize all my configs and store it in memory in some struct object. And then I should have access to configmanager object and should be able to access all those configs from my main function using some getters maybe?
Below is my configmanager go class. It is very simple for now to make it easier to understand.
package config
import (
"encoding/json"
"fmt"
"io/ioutil"
"github.com/david/internal/utilities"
)
type ClientMetrics struct {
ClientMetrics []ClientMetric `json:"clientMetrics"`
}
type CustomerData struct {
Process []string `json:"Process"`
Mat []string `json:"Mat"`
}
type ClientMetric struct {
ClientID int `json:"clientId"`
CustomerData CustomerData `json:"customerData,omitempty"`
LegCustomer []int `json:"legCustomer"`
OtherIds []int `json:"otherIds,omitempty"`
CatID int `json:"catId,omitempty"`
}
func Init(root string) (err error) {
files, err := utilities.FindFiles(root, "process-data.json")
if err != nil {
return fmt.Errorf("cannot find process-data.json file: %v", err)
}
for _, file := range files {
body, err := ioutil.ReadFile(file)
if err != nil {
return fmt.Errorf("unable to read file: %v", err)
}
var auto ClientMetrics
json.Unmarshal(body, &auto)
}
return nil
}
And here is I use it in my main function - This is just basic code just to demonstrate what I am doing but this isn't production ready code.
package main
import (
"github.com/david/internal/config"
)
func main() {
root := "/home/Configurations"
config.Init(root)
//
}
In my above Init function, I find process-data.json file if it is there on the disk and then load it in memory by deserializing it into ClientMetrics object. Everything works fine as shown above.
Problem Statement
Since I am coming from Java and C# background, so my confusion is how can I make an object of configmanager class and how should I initialize all my configs during the first time when I call configmanager and also have access to ClientMetrics struct using some getters. In Java and C#, I used to have constructor where I initialize all these things and then have some getters to access the config. How should I do the same thing in golang.
My main confusion is do we have constuctors in go and how should I make getters to access struct object in my main function? I am just looking for better design in go and how should I do my above code in golang?
Update
I think I wasn't able to explain properly. I have X (let's suppose 5 for now) different json files in a folder and each of those json files needs to have their own struct because they are totally different json files. My configmanager file will be responsible to load all those 5 json files into their own struct and then I should be able to access all those 5 structs and their fields from the object of configmanager. All this should happen during the initialization of configmanager class when it is called for the first time.
Here is just an example where I have bunch of json files in their own corresponding folder (folderX). I have three categories of files (clientMap-*.json, dataMap-*.json, process-data.json) as shown below.
Products
├── folder1
│ ├── clientMap-123.json
│ ├── clientMap-987.json
│ ├── clientMap-7161.json
├── folder2
│ ├── dataMap-18271.json
│ ├── dataMap-12921.json
│ ├── dataMap-98121.json
└── folder3
├── process-data.json
Now I need to read all these files (clientMap-*.json, dataMap-*.json, process-data.json) in their own struct. And I should able to use configmanager class to get corresponding struct and their fields too after unmarshalling.
For example: Read clientMap-*.json files.
files, err := utilities.FindFiles(root, "clientMap-*.json")
// load all clientMap-*.json files in its own struct after unmarshalling
Similarly for dataMap-*.json files
files, err := utilities.FindFiles(root, "dataMap-*.json")
// load all dataMap-*.json files in its own struct after unmarshalling
Also for process-data.json files
files, err := utilities.FindFiles(root, "process-data.json")
// load all process-data.json files in its own struct after unmarshalling
My FindFiles method will find all the files even with regex like above. files variable is an array containing list of all files matching particular pattern. Now I can create ConfigManager struct type holding all other structs for my config but I am trying to find a solution which is easily extensible so that in future if I add any other json file category it should be able to extend easily. What is the correct way to solve this?
This question is hard to answer because you kind of forgot to ask one in a definitive way, so I will start my answer by extracting a question based on what you wrote. I believe we can do so from this:
Problem Statement
[...] my confusion is how can I make an object of configmanager class and how should I initialize all my configs during the first time when I call configmanager and also have access to ClientMetrics struct using some getters
I believe the real question here is "How do I separate the concern of reading and unmarshalling the files from the concern of storing the results for my program to use?".
It's common to separate concerns by breaking things up into multiple functions/methods, which you have already done to some extent. Storage however is strictly a matter of types, so we need a type capable of holding the results. I'll use this opportunity to omit the word Manager from the type's name, because all it does is to provide useless abstraction. This type does not manage the config. It is the config, in that it contains all of the config.
type Config struct {
ClientMapConfigs []ClientMapConfig
DataMapConfigs []DataMapConfig
ProcessDataConfigs []ProcessDataConfig
}
Notice the fields start with an upper-case letter, making them public. This communicates that there could be nonsense in there, as nothing is protected from writes, which aligns with the fact that we read this data from files. A correct program must validate this data before using it. You could then communicate the validity of the validated data in the variable name.
func main() {
validConfig := getValidConfig("path/to/dir")
// ...
}
func getValidConfig(configDirectoryPath string) *Config {
config, err := NewConfigFromConfigDirectory(configDirectoryPath)
if err != nil {
log.Printf("Failed to read config from dir '%s': %v\n", configDirectoryPath, err)
os.Exit(1)
}
if err = ValidateConfig(config); err != nil {
log.Printf("Config from dir '%s' failed to validate: %v\n", configDirectoryPath, err)
os.Exit(1)
}
}
func NewConfigFromConfigDirectory(configDirectoryPath string) *Config {
// <- read individual configs and add to slices here
return &Config{ // This is the closest to a "constructor" that Go has.
ClientMapConfigs: clientMapConfigs,
DataMapConfigs: dataMapConfigs,
ProcessDataConfigs: processDataConfigs,
}
}
Notice that there is no imminent need for the functions validating and reading the config to have a receiver, i.e. be a method of a struct. They're fine as standalone functions until your requirements change in ways which demand the introduction of statefulness for either logic.
Also I use exit code 1 for the error-cases here, because Golang uses code 2 when the program terminates due to a panic. The former can be thought of as a problem with the environment, while the later indicates a problem within the program itself. It's a useful distinction to make, and aligns with the semantics of Exception versus RuntimeException which you may know from Java.
I can have let's say 10 different configs (files) and each of those configs can have their own structs since it's a different configs so I need to have separate struct for them
That looks like dynamic JSON struct unmarshalling, which was presented in 2015 by John Asmuth in decoding with mixed structures
You can run the following example here.
type Dog struct {
Name string
Frisbees int
}
type Cat struct {
Name string
Strings int
}
type RawAnimal struct {
Type string
Cat
Dog
}
type Animal RawAnimal
func (a *Animal) UnmarshalJSON(data []byte) error {
if err := json.Unmarshal(data, (*RawAnimal)(a)); err != nil {
return err
}
switch a.Type {
case "cat":
return json.Unmarshal(data, &a.Cat)
case "dog":
return json.Unmarshal(data, &a.Dog)
}
return fmt.Errorf("unknown type %q", a.Type)
}
From there, your ConfigManager will instantiate the right Config structure, depending on the raw JSON read.
I think the trouble is that you're looking at Go from a Java/C# perspective and thus struggling to make sense of the features. If you have the time, then I would suggest that you go thru some Go tutorials or books before you start coding (this one is pretty good: https://www.amazon.com/Programming-Language-Addison-Wesley-Professional-Computing/dp/0134190440)
To answer your question directly, what you need to do is to create a function which returns a pointer to an object of the struct (see here for a simple example: https://gobyexample.com/structs)
Taking ClientMetric as an example:
func NewClientMetric(ClientID int, CustomerData CustomerData, LegCustomer []int, OtherIds []int, CatID int) (*ClientMetric, error) {
//validate your inputs
//in case of errors, create and error message in the variable err and then: return nil, err
//other code which would go into a typical constructor would go here
return &ClientMetric{ClientID,CustomerData, LegCustomer, OtherIds, CatID}, nil
}
In this case, the function NewClientMetric is the constructor and it returns a pointer/reference to the newly created object. It also returns an error object, which is the same as saying that the constructor throws exceptions. Just as you would need to use try/catch in Java, you would need to check to handle the err variable returned by this function.
You would need to make similar functions for each of your types.
As for getters & setters, generally speaking, that should be avoided in Go. You can access the attributes of a struct directly. A function (like a getter) is only useful if you're going to do something to the attribute before returning it. Something like this:
type Customer struct {
FirstName string
LastName string
}
func (this *Customer) GetFullName() string {
return this.FirstName + " " + this.LastName
}
and these can then be accessed like this:
var customer *Customer
customer = &Customer{"John","Smith")
fmt.Println(customer.FirstName)
fmt.Println(customer.LastName)
fmt.Println(customer.GetFullName())
Please note that attributes, functions and methods which begin with a capital letter are public, the others are private. If FirstName was written as firstName, it would not be accessible outside the package in which it was declared.
Please do note that I'm not checking for errors if the pointer is null/nil, but that is always recommended.

Yaml file to struct parsing (translation) in Golang

I'm trying to build a translation feature for my webapp. There are multiple packages in my app. Each package(directory) contains a translation folder and yaml files inside. But I have a problem with parsing and assign it to messages.
en.yaml
msgLogin : "You've login successfully"
msgProducts:
0: "You don't have any product."
1: "You have %d product."
2: "You have %d products."
errLogin: "Wrong password or username"
my code:
type TranslationEntry struct {
Key struct {
Value interface{}
}
}
func parseTranslations(dir string) {
files, _ := ioutil.ReadDir(dir)
for _, f := range files {
yamlFile, _ := ioutil.ReadFile(dir + "/" + f.Name())
var data translation
if err := yaml.Unmarshal(yamlFile, &data); err != nil {
return nil, err
}
lang := strings.Split(f.Name(), ".")[0]
switch msg := data.Key.Value.(type) {
case string:
message.SetString(language.Make(lang), cast.ToString(data.Key), cast.ToString(data.Key.Value))
case map[int]string:
message.Set(language.Make(lang), cast.ToString(data.Key),
plural.Selectf(1, "%d",
"=0", cast.ToString(data.Key.Value["0"]),
"=1", cast.ToString(data.Key.Value["1"]),
"=2", cast.ToString(data.Key.Value["2"]),
))
}
translations[lang] = &dictionary{Data: data}
}
}
I'm totally lost about how to design my struct or parse yaml file.
Thank you in advanced
If you're using the YAML library I think you're using (https://godoc.org/gopkg.in/yaml.v2), to make a Golang struct which can use Unmarshal to map from the YAML file in your post you can do this:
type TranslationEntry struct {
MsgLogin string `yaml:"msgLogin"`
MsgProducts map[int]string `yaml:"msgProducts"`
ErrLogin string `yaml:"errLogin"`
}
The things inside the `` after the field declarations are called tags. They're the way field names are usually specified when mapping between some datatype and a Golang struct (in my case usually I map a struct to JSON, but I've also done YAML). If you're using the same YAML parser I mentioned above, this is how it works.
Basically the text inside the double quotes is the YAML key to which your struct field will be mapped. In the above code the only difference between the YAML key name and the struct field name is capitalization, but here is an example using totally different names:
type ExampleStruct struct {
MyAbcField string `yaml:"abc"`
}
This will set the value of MyAbcField to "my data" when using Unmarshal with ExampleStruct and the following YAML:
abc: "my data"
This allows you to design a Golang struct which matches however you decide to structure your YAML.
Here's my above code in Go Playground: https://play.golang.org/p/Q9FvNsw-BOx
Now, if you are unable to fix a structure for your YAML files, you can also parse into nested maps. You can do this by passing a variable of type interface{} (empty interface) to Unmarshal instead of a struct. However, this requires a lot of boilerplate because you will need to do type assertions to access your data. Thus I recommend using a fixed structure instead unless you absolutely can't avoid it.
Here's an example where I parse the YAML you posted and then get the msgLogin field:
var data interface{}
if err := yaml.Unmarshal([]byte(yamlFile), &data); err != nil {
// handle error
}
// a type assertion that data is a map is needed in order to get keys or iterate
topLevel, ok := data.(map[interface{}]interface{})
if !ok {
// handle error
}
fmt.Println(topLevel["msgLogin"])
And here's the Go Playground of my struct example changed to use parsing into a nested map instead: https://play.golang.org/p/ERBjClSazkz

How to use github.com/jhump/protoreflect to parse a byte array of proto message knowing the message descriptor

I have a file containing a slice of bytes of the following protomessage.
syntax = "proto3";
package main;
message Address {
string street = 1;
string country = 2;
string state = 3;
}
I have the message type described as follow:
func GetProtoDescriptor() (*descriptor.DescriptorProto, error) {
return &descriptor.DescriptorProto{
Name: proto.String("Address"),
Field: []*descriptor.FieldDescriptorProto{
&descriptor.FieldDescriptorProto{
Name: proto.String("street"),
JsonName: proto.String("street"),
Number: proto.Int(1),
Label: descriptor.FieldDescriptorProto_LABEL_OPTIONAL.Enum(),
Type: descriptor.FieldDescriptorProto_TYPE_STRING.Enum(),
},
&descriptor.FieldDescriptorProto{
Name: proto.String("state"),
JsonName: proto.String("state"),
Number: proto.Int(2),
Label: descriptor.FieldDescriptorProto_LABEL_OPTIONAL.Enum(),
Type: descriptor.FieldDescriptorProto_TYPE_STRING.Enum(),
},
&descriptor.FieldDescriptorProto{
Name: proto.String("country"),
JsonName: proto.String("country"),
Number: proto.Int(2),
Label: descriptor.FieldDescriptorProto_LABEL_OPTIONAL.Enum(),
Type: descriptor.FieldDescriptorProto_TYPE_STRING.Enum(),
},
},
}, nil
}
I would like to know how best I can use jhump/protoreflect to parse the content of the file using the message descriptor above.
Thank you for your help.
The typical way would be to compile the protocol to Go code using protoc:
protoc main.proto --go_out=.
This will generate main.pb.go, which will have a type called Address:
var addr Address
err := proto.Unmarshal(bytes, &addr)
If this for some reason is not do-able (e.g. you must do it dynamically, with a descriptor), there are options. (But it's a lot more complicated.)
Use a protoc-generated descriptor. You can have protoc export to a descriptor file (via the -o flag). If this is for RPC, have the server use server reflection; you can then use the github.com/jhump/protoreflect/grpcreflect package to download the server's descriptor (presumably generated by protoc).
If you must create the descriptor programmatically, instead of using protoc, I recommend using the github.com/jhump/protoreflect/desc/builder package to construct it (instead of trying to create raw protos by hand). For example, your raw protos are not sufficient since they have no parent FileDescriptorProto, which is the root of any descriptor hierarchy. That builder package can take care of details like that for you (e.g. it will synthesize a parent file descriptor if necessary).
In any event, the descriptor you want is a desc.Descriptor (from the github.com/jhump/protoreflect/desc package). The tips above (using other protoreflect sub-packages) will return desc.Descriptor instances. If you only have the raw descriptor protos (like in your example code), you'd want to first turn your *descriptor.FileDescriptorProto into a *desc.FileDescriptor using the desc#CreateFileDescriptor function.
If you are using protoc and its -o option to create a descriptor set file (don't forget --include_imports flag, too), you can load it and turn it into a *desc.FileDescriptor using the desc#CreateFileDescriptorFromSet function. Here's an example:
bytes, err := ioutil.ReadFile("protoset-from-protoc")
if err != nil {
panic(err)
}
var fileSet descriptor.FileDescriptorSet
if err := proto.Unmarshal(bytes, &fileSet); err != nil {
panic(err)
}
fd, err := desc.CreateFileDescriptorFromSet(&fileSet)
if err != nil {
panic(err)
}
// Now you have a *desc.FileDescriptor in `fd`
Once you have that right kind of descriptor, you can create a *dynamic.Message. You can then unmarshal into that using either the proto#Unmarshal function or using the dynamic message's Unmarshal method.

"Merge" fields two structs of same type

Looking at this struct:
type Config struct {
path string
id string
key string
addr string
size uint64
}
Now I have a DefaultConfig intialized with some values and one loaded from a file, let's say FileConfig.
I want both structs to me merged, so that I get a Config with the content of both structs. FileConfig should override anything set in DefaultConfig, while FileConfig may not have all fields set.
(Why this? Because a potential user may not know the default value, so removing that entry would be equivalent to setting the default - I think)
I thought I'd need reflection for this:
func merge(default *Config, file *Config) (*Config) {
b := reflect.ValueOf(default).Elem()
o := reflect.ValueOf(file).Elem()
for i := 0; i < b.NumField(); i++ {
defaultField := b.Field(i)
fileField := o.Field(i)
if defaultField.Interface() != reflect.Zero(fileField.Type()).Interface() {
defaultField.Set(reflect.ValueOf(fileField.Interface()))
}
}
return default
}
Here I am not sure:
If reflection is needed at all
There may be easier ways to do this
Another issue I see here is that checking for zero values may be tricky: what if the overriding struct intends to override with a zero value? Luckily, I don't think it applies to my case - but this becomes a function, it may become a problem later
Foreword: The encoding/json package uses reflection (package reflect) to read/write values, including structs. Other libraries also using reflection (such as implementations of TOML and YAML) may operate in a similar (or even in the same way), and thus the principle presented here may apply to those libraries as well. You need to test it with the library you use.
For simplicity, the solution presented here uses the standard lib's encoding/json.
An elegant and "zero-effort" solution is to use the encoding/json package and unmarshal into a value of the "prepared", default configuration.
This handles everything you need:
missing values in config file: default applies
a value given in file overrides default config (whatever it was)
explicit overrides to zero values in the file takes precedence (overwrites non-zero default config)
To demonstrate, we'll use this config struct:
type Config struct {
S1 string
S2 string
S3 string
S4 string
S5 string
}
And the default configuration:
var defConfig = &Config{
S1: "", // Zero value
S2: "", // Zero value
S3: "abc",
S4: "def",
S5: "ghi",
}
And let's say the file contains the following configuration:
const fileContent = `{"S2":"file-s2","S3":"","S5":"file-s5"}`
The file config overrides S2, S3 and the S5 fields.
Code to load the configuration:
conf := new(Config) // New config
*conf = *defConfig // Initialize with defaults
err := json.NewDecoder(strings.NewReader(fileContent)).Decode(&conf)
if err != nil {
panic(err)
}
fmt.Printf("%+v", conf)
And the output (try it on the Go Playground):
&{S1: S2:file-s2 S3: S4:def S5:file-s5}
Analyzing the results:
S1 was zero in default, was missing from file, result is zero
S2 was zero in default, was given in file, result is the file value
S3 was given in config, was overriden to be zero in file, result is zero
S4 was given in config, was missing in file, result is the default value
S5 was given in config, was given in file, result is the file value
Reflection is going to make your code slow.
For this struct I would implement a straight Merge() method as:
type Config struct {
path string
id string
key string
addr string
size uint64
}
func (c *Config) Merge(c2 Config) {
if c.path == "" {
c.path = c2.path
}
if c.id == "" {
c.id = c2.id
}
if c.path == "" {
c.path = c2.path
}
if c.addr == "" {
c.addr = c2.addr
}
if c.size == 0 {
c.size = c2.size
}
}
It's almost same amount of code, fast and easy to understand.
You can cover this method with uni tests that uses reflection to make sure new fields did not get left behind.
That's the point of Go - you write more to get fast & easy to read code.
Also you may want to look into go generate that will generate the method for you from struct definition. Maybe there event something already implemented and available on GitHub? Here is an example of code that do something similar: https://github.com/matryer/moq
Also there are some packages on GitHub that I believe are doing what you want in runtime, for example: https://github.com/imdario/mergo
Another issue I see here is that checking for zero values may be
tricky: what if the overriding struct intends to override with a zero
value?
In case you cannot utilize encoding/json as pointed out by icza or other format encoders that behave similarly you could use two separate types.
type Config struct {
Path string
Id string
Key string
Addr string
Size uint64
}
type ConfigParams struct {
Path *string
Id *string
Key *string
Addr *string
Size *uint64
}
Now with a function like this:
func merge(conf *Config, params *ConfigParams)
You could check for non-nil fields in params and dereference the pointer to set the value in the corresponding fields in conf. This allows you to unset fields in conf with non-nil zero value fields in params.
This solution won't work for your specific question, but it might help someone who has a similar-but-different problem:
Instead of creating two separate structs to merge, you can have one "default" struct, and create a modifier function for it. So in your case:
type Config struct {
path string
id string
key string
addr string
size uint64
}
var defcfg = Config {
path: "/foo",
id: "default",
key: "key",
addr: "1.2.3.4",
size: 234,
}
And your modifier function:
func myCfg(c *Config) {
c.key = "different key"
}
This works in tests where I want to test many small, different variations of a largely unmodified struct:
func TestSomething(t *testing.T) {
modifiers := []func (*Config){
.... // modifier functions here
}
for _, f := range modifiers {
tc := defcfg // copy
f(&tc)
// now you can use tc.
}
}
Not useful when you read your modified config from a file into a struct, though. On the plus side: this also works with zero values.

Go-yaml control characters are not allowed error

I'm trying to make a really simple ssh address book program. Takes some info about ssh addresses and stores them in a yaml document. I'm doing it partly to learn a bit about Go and am having a small problem. I can serialize the data and put a document into a file but I'm getting this error when I try to read it back:
yaml: control characters are not allowed
I'm not sure what this error message means, googling didn't yield any helpful results. Any ideas?
These are the structs I'm using to organize the data:
type EntriesList struct {
SSHEntries []SSHEntry `yaml:"sshentries"`
}
type SSHEntry struct {
Name string `yaml:"name"`
Command SSHCmd `yaml:"command"`
}
type SSHCmd struct {
Addr string `yaml:"addr"`
Port int `yaml:"port"`
Uname string `yaml:"uname"`
}
The format it puts my data into is:
---
entrieslist:
- name: entry1
command:
addr: somewhere
port: 22
uname: someone
- name: entry2 ... etc
I checked this ^^ with a YAML validator and it is legal YAML.
Here is my function to read the file:
// CONF is the path to the file
func readConf(CONF string) *EntriesList {
configFile := openConfigFile(CONF)
defer configFile.Close()
buffer := make([]byte, 512, 512)
_, err := configFile.Read(buffer)
check(err)
var entries EntriesList
err = yaml.Unmarshal(buffer, &entries)
data, _ := yaml.Marshal(entries)
fmt.Println(string(data))
return &entries
}
Figured it out, the problem was that my buffer was too big. If you have a []byte that is too big then go-yaml will read those extra bytes as characters and throw errors. I changed my code to :
func readConf(CONF string) *EntriesList {
confiFile, err := ioutil.ReadFile(CONF)
check(err)
var entries EntriesList
err = yaml.Unmarshal(confiFile, &entries)
check(err)
return &entries
}
And it worked as expected.
From the sounds of it, you're hitting the error block at https://github.com/go-yaml/yaml/blob/53feefa2559fb8dfa8d81baad31be332c97d6c77/readerc.go#L347 , and it looks like that should also be telling you the offset (where in the file it's hitting the problematic character) and character code. If that info is enough to solve your problem, then great. If, on the other hand, the yaml library is spitting out validated yaml that it is not comfortable accepting as input, you should open an issue with the maintainer on Github at https://github.com/go-yaml/yaml/issues
I know this came over-late, but using the function from the main question, one can also use the number of bytes read into the buffer to slice the data.
// CONF is the path to the file
func readConf(CONF string) *EntriesList {
configFile := openConfigFile(CONF)
defer configFile.Close()
buffer := make([]byte, 512, 512)
n, err := configFile.Read(buffer)
check(err)
var entries EntriesList
err = yaml.Unmarshal(buffer[:n], &entries)
data, _ := yaml.Marshal(entries)
fmt.Println(string(data))
return &entries
}

Resources