I am attempting to learn goyaml, and am having some issues with attempting to produce slices from yaml. I can marshal my struct into this example yaml, but I cannot unmarshal the same yaml back into the struct.
input:
file:
- file: stdin
webservice:
- port: "0000"
address: 0.0.0.0
processing:
regex:
- regex: ^(this)*
mapping: (1) thing
output:
file:
- file: stdout
webservice:
- port: "0000"
address: 0.0.0.0
struct:
type Configuration struct{
Input ioInstance
Processing ProcessingInstance
Output ioInstance
}
type ioInstance struct {
File []FileInstance
Webservice []WebserviceInstance
}
type FileInstance struct {
File string
}
type WebserviceInstance struct {
Port string
Address string
}
type ProcessingInstance struct {
Regex []RegexInstance
}
type RegexInstance struct {
Regex string
Mapping string
}
When testing unmarshaling I'm getting reflect errors, as I understand it I think I may need to re-approach the design without struct slices, I was hoping someone could offer some insight into this?
errors:
panic: reflect: reflect.Value.Set using unaddressable value [recovered]
panic: reflect: reflect.Value.Set using unaddressable value [recovered]
panic: reflect: reflect.Value.Set using unaddressable value
The following code works just fine for me, outputting the original yaml text as it should:
package main
import "fmt"
import "gopkg.in/yaml.v2"
func main() {
c := &Configuration{}
yaml.Unmarshal([]byte(yamlText), c)
buf, _ := yaml.Marshal(c)
fmt.Println(string(buf))
}
var yamlText = `
input:
file:
- file: stdin
webservice:
- port: "0000"
address: 0.0.0.0
processing:
regex:
- regex: ^(this)*
mapping: (1) thing
output:
file:
- file: stdout
webservice:
- port: "0000"
address: 0.0.0.0
`
type Configuration struct {
Input ioInstance
Processing ProcessingInstance
Output ioInstance
}
type ioInstance struct {
File []FileInstance
Webservice []WebserviceInstance
}
type FileInstance struct {
File string
}
type WebserviceInstance struct {
Port string
Address string
}
type ProcessingInstance struct {
Regex []RegexInstance
}
type RegexInstance struct {
Regex string
Mapping string
}
Output:
input:
file:
- file: stdin
webservice:
- port: "0000"
address: 0.0.0.0
processing:
regex:
- regex: ^(this)*
mapping: (1) thing
output:
file:
- file: stdout
webservice:
- port: "0000"
address: 0.0.0.0
(Of course you should not ignore errors in your actual code, like I'm doing here.)
EDIT:
Unmarshal
needs a pointer to a struct in order to set its fields. If you call it with a plain struct value, it receives only a copy of the original struct (because in Go everything is passed as a copy), and therefore couldn't possibly alter the fields of the original struct.
Therefore, you have basically two options:
You can define and initialize c
with c := &Configuration{}
, i.e. defining it as a pointer to type Configuration
while simultaneously pointing it to a new, zeroed Configuration
value. Then you can call yaml.Unmarshal([]byte(yamlText), c)
.
Alternatively you can define c
with var c Configuration
, which means c
is not a pointer, but a new zero value of type Configuration
. In this case you need to explicitly pass a pointer to that value when you call Unmarshal
: yaml.Unmarshal([]byte(yamlText), &c)
.
Note that the pointer you pass to Unmarshal must point to an existing Configuration
value. var c *Configuration
would define c
as a pointer, but passing it immediately to Unmarshal
would lead to an panic, as it's value is nil
; it doesn't point to an existing Configuration
value.
Also, in my code above there was originally a little typo, which is now fixed (though the code still worked). I wrote both c := &Configuration{}
and yaml.Unmarshal([]byte(yamlText), &c)
, so I actually passed Unmarshal
a pointer to a pointer to a struct, which Unmarshal
was able to handle without issues.