Go: 将嵌套结构解组为 interface{}

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

Go: XML Unmarshal Nested Structs into interface{}

问题

我来自Python背景,这是我第一次正式涉足Go,所以我觉得还没有完全理解。

我目前正在使用Go实现Affiliate Window XML API。该API遵循请求和响应的标准结构,因此我试图保持代码的DRY原则。信封的结构始终相同,类似于这样:

<Envelope>
    <Header></Header>
    <Body></Body>
</Envelope>

HeaderBody的内容根据请求和响应的不同而不同,因此我创建了一个基本的Envelope结构体:

type Envelope struct {
    XMLName xml.Name    `xml:"http://schemas.xmlsoap.org/soap/envelope/ Envelope"`
    NS1     string      `xml:"xmlns:ns1,attr"`
    XSD     string      `xml:"xmlns:xsd,attr"`
    Header  interface{} `xml:"http://schemas.xmlsoap.org/soap/envelope/ Header"`
    Body    interface{} `xml:"Body"`
}

这对于将请求的XML进行编组很有效,但是在解组时我遇到了问题:

func NewResponseEnvelope(body interface{}) *Envelope {
    envelope := NewEnvelope()
    envelope.Header = &ResponseHeader{}
    envelope.Body = body
    return envelope
}

func main() {
    responseBody := &GetMerchantListResponseBody{}
    responseEnvelope := NewResponseEnvelope(responseBody)

    b := bytes.NewBufferString(response)
    xml.NewDecoder(b).Decode(responseEnvelope)
    fmt.Println(responseEnvelope.Header.Quota) // 为什么我无法访问这个?
}

这个链接http://play.golang.org/p/v-MkfEyFPM 可能更好地描述了问题,比我用文字描述要好:p

谢谢,

Chris

英文:

I come from a Python background and this is my first proper foray into Go so I don't think things are clicking quite yet.

I'm currently implementing the Affiliate Window XML API in Go. The API follows a standard structure for Requests and Responses so to that end I'm trying to keep things dry. Envelopes always have the same structure, something like this:

<!-- language: lang-xml -->

&lt;Envelope&gt;
    &lt;Header&gt;&lt;/Header&gt;
    &lt;Body&gt;&lt;/Body&gt;
&lt;/Envelope&gt;

The contents Header and Body will be different depending on what I'm requesting and the response so I created a base Envelope struct

<!-- language: lang-golang -->

type Envelope struct {
	XMLName xml.Name    `xml:&quot;http://schemas.xmlsoap.org/soap/envelope/ Envelope&quot;`
	NS1     string      `xml:&quot;xmlns:ns1,attr&quot;`
	XSD     string      `xml:&quot;xmlns:xsd,attr&quot;`
	Header  interface{} `xml:&quot;http://schemas.xmlsoap.org/soap/envelope/ Header&quot;`
	Body    interface{} `xml:&quot;Body&quot;`
}

This works well for marshaling the XML for a request but I'm having problems with unmarshaling:

<!-- language: lang-golang -->

func NewResponseEnvelope(body interface{}) *Envelope {
	envelope := NewEnvelope()
	envelope.Header = &amp;ResponseHeader{}
	envelope.Body = body
	return envelope
}

func main() {
	responseBody := &amp;GetMerchantListResponseBody{}
	responseEnvelope := NewResponseEnvelope(responseBody)

	b := bytes.NewBufferString(response)
	xml.NewDecoder(b).Decode(responseEnvelope)
	fmt.Println(responseEnvelope.Header.Quota) // Why can&#39;t I access this?
}

This http://play.golang.org/p/v-MkfEyFPM probably describes the problem better in code than I can in words :p

Thanks,

Chris

答案1

得分: 2

Envelope 结构体内的 Header 字段的类型是 interface{}不是一个 struct,因此你不能引用它的任何字段。

为了引用名为 Quota 的字段,你需要声明一个包含 Quota 字段的静态类型的 Header,类似于以下代码:

type HeaderStruct struct {
    Quota string
}

type Envelope struct {
    // 其他字段省略
    Header HeaderStruct
}

如果你不知道它的具体类型,或者无法确定一个单一的类型,你可以将其保留为 interface{},但是你需要在运行时使用 类型断言类型切换 将其转换为静态类型,后者的代码示例如下:

headerStruct, ok := responseEnvelope.Header.(HeaderStruct)
// 如果 ok 为 true,则 headerStruct 的类型是 HeaderStruct
// 否则 responseEnvelope.Header 的类型不是 HeaderStruct

另一种选择是使用反射来访问 Envelope.Header 值的命名字段,但如果可能的话,请尝试使用其他方法解决。如果你对在 Go 中使用反射感兴趣,我建议先阅读 The Laws of Reflection 博文。

英文:

The type of the Header field inside the Envelope struct is interface{} which is not a struct so you cannot refer to any of its fields.

In order to refer to a field named Quota, you have to declare Header with a static type which contains a Quota field, something like this:

type HeaderStruct struct {
    Quota string
}

type Envelope struct {
    // other fields omitted
    Header HeaderStruct
}

If you don't know what type it will be or you can't commit to a single type, you can leave it as interface{}, but then you have to use either Type switches or Type assertion to convert it to a static type at runtime, the latter would look something like this:

headerStruct, ok := responseEnvelope.Header.(HeaderStruct)
// if ok is true, headerStruct is of type HeaderStruct
// else responseEnvelope.Header is not of type HeaderStruct

Another option would be to use reflection to access named fields of the Envelope.Header value, but try to solve it in other ways if possible. If you're interested in learning more about reflection in Go, I recommend reading The Laws of Reflection blog post first.

huangapple
  • 本文由 发表于 2015年1月20日 20:33:25
  • 转载请务必保留本文链接:https://go.coder-hub.com/28045510.html
匿名

发表评论

匿名网友

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

确定