GoYAML 结构切片

huangapple go评论102阅读模式
英文:

GoYAML struct slices

问题

我正在尝试学习goyaml,并且在尝试从yaml中生成切片时遇到了一些问题。我可以将我的结构体编组成这个示例yaml,但是我无法将相同的yaml解组回结构体中。

结构体:

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
}

在测试解组时,我遇到了反射错误,据我了解,我认为我可能需要重新设计,不使用结构体切片,我希望有人能提供一些见解。

错误信息:

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
英文:

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

答案1

得分: 4

以下是翻译好的内容:

以下代码对我来说完全正常,按照应该的方式输出原始的yaml文本:

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
}

输出:

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

(当然,在实际代码中,你不应该忽略错误,就像我在这里做的一样。)

编辑:

Unmarshal 需要一个指向结构体的指针来设置其字段。如果你使用一个普通的结构体值调用它,它只会接收到原始结构体的一个副本(因为在 Go 中,所有东西都是按值传递的),因此无法修改原始结构体的字段。

因此,你基本上有两个选择:

你可以使用 c := &Configuration{} 来定义和初始化 c,即将其定义为指向 Configuration 类型的指针,同时将其指向一个新的、零值的 Configuration 值。然后你可以调用 yaml.Unmarshal([]byte(yamlText), c)

或者你可以使用 var c Configuration 来定义 c,这意味着 c 不是一个指针,而是 Configuration 类型的新的零值。在这种情况下,当你调用 Unmarshal 时,你需要显式地传递一个指向该值的指针:yaml.Unmarshal([]byte(yamlText), &c)

注意,你传递给 Unmarshal 的指针必须指向一个已存在的 Configuration 值。var c *Configuration 会将 c 定义为一个指针,但是立即将其传递给 Unmarshal 会导致恐慌,因为它的值是 nil;它没有指向一个已存在的 Configuration 值。

另外,在我上面的代码中最初有一个小错误,现在已经修复了(尽管代码仍然可以工作)。我写了 c := &Configuration{}yaml.Unmarshal([]byte(yamlText), &c),所以我实际上传递给 Unmarshal 的是 指向指针的指针,而 Unmarshal 能够处理这个问题。

英文:

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.

huangapple
  • 本文由 发表于 2016年3月13日 04:02:45
  • 转载请务必保留本文链接:https://go.coder-hub.com/35962661.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定