接口和encoding/xml解组

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

Interfaces and encoding/xml Unmarshal

问题

我正在写一个与SOAP服务交互的SOAP服务。SOAP API 的一部分是返回查询结果的,我希望提供用于解码信封的基本结构体,同时允许开发人员填写编码/解码为 XML 的接口。

type QueryEnvelope struct {
    XMLName xml.Name   `xml:"http://schemas.xmlsoap.org/soap/envelope/ Envelope"`
    Body    *QueryBody `xml:"http://schemas.xmlsoap.org/soap/envelope/ Body"`
}

type QueryBody struct {
    QueryResult *QueryResult `xml:"queryResponse>result"`
}

type QueryResult struct {
    Done    bool    `xml:"done"`
    Size    int     `xml:"size"`
    Records Record `xml:"records"`
}

type Record interface {
    UnmarshalXML(d *xml.Decoder, start xml.StartElement) error
}

是否可以像这样注入一个用于解码的接口,还是必须在 QueryEnvelope{} 级别接受接口?

理想情况下,客户端代码应该如下所示:

type Record struct {
    Id   int    `xml:"id"`
    Name string `xml:"name"`
}

func (r *Record) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {
    // 在这里进行解码,或者最好的情况下,我甚至不需要实现 UnmarshalXML()!
}

res, err := Query("select id from table", Record{})

这意味着他们不需要重现 QueryEnvelope 结构体(作为使用我创建的包的开发人员)。

英文:

I have a soap service I'm writing against. One portion of the soap API is for returning a query result, and I'm hoping to provide the base structs for decoding the envelope, while allow a developer to fill in the interface in which encoding/xml will decode to.

type QueryEnvelope struct {
	XMLName xml.Name   `xml:"http://schemas.xmlsoap.org/soap/envelope/ Envelope"`
	Body    *QueryBody `xml:"http://schemas.xmlsoap.org/soap/envelope/ Body"`
}

type QueryBody struct {
	QueryResult *QueryResult `xml:"queryResponse>result"`
}

type QueryResult struct {
	Done    bool    `xml:"done"`
	Size    int     `xml:"size"`
	Records Record `xml:"records"`
}

type Record interface {
	UnmarshalXML(d *xml.Decoder, start xml.StartElement) error
}

Is it possible to inject an interface like this for unmarhsalling or do I have to accept the interface at the QueryEnvelope{} level?

Ideally client side would act as such:

type Record struct {
     Id int `xml:"id"`,
     Name stirng `xml:"name"`
}

func (r *Record) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {
     // unmasrshal here, or best case I dont even need to implment UnmarsahlXML()!
}
res, err := Query("select id from table", Record{})

This would mean that they don't have to reproduce the QueryEnvelope struct (as a developer using the package I'm creating)

答案1

得分: 1

常见的解决方案是要求客户端传递一个指向他的结构体实例的指针,如下所示:

// 客户端的自定义结构体
type ClientStruct struct {
    Id   int    `xml:"id"`
    Name string `xml:"name"`
}

// 这将是你的 API
func Query(foo string, v interface{}) {
    fakeXmlResult := "<test><id>012345</id><name>MyName</name></test>"
    xml.Unmarshal([]byte(fakeXmlResult), v)
}

func main() {
    r := ClientStruct{}
    Query("SQL QUERY", &r) // 注意这里的 &
    fmt.Println(r)
}

我正确理解了你的问题吗?

英文:

The common solution is to ask the client to pass a pointer to his struct instance like so:

// The custom struct of your client
type ClientStruct struct {
	Id   int    `xml:&quot;id&quot;`
	Name string `xml:&quot;name&quot;`
}

// This would be your API
func Query(foo string, v interface{}) {
	fakeXmlResult := &quot;&lt;test&gt;&lt;id&gt;012345&lt;/id&gt;&lt;name&gt;MyName&lt;/name&gt;&lt;/test&gt;&quot;
	xml.Unmarshal([]byte(fakeXmlResult), v)
}

func main() {
	r := ClientStruct{}
	Query(&quot;SQL QUERY&quot;, &amp;r) // Note the &amp;
	fmt.Println(r)
}

Did I understand your question correctly?

答案2

得分: 0

所以你确实可以在中间插入一个接口,我在为解码器分配内存时失败了:

包:

type QueryEnvelope struct {
    XMLName xml.Name   `xml:"http://schemas.xmlsoap.org/soap/envelope/ Envelope"`
    Body    *QueryBody `xml:"http://schemas.xmlsoap.org/soap/envelope/ Body"`
}

type QueryBody struct {
    QueryResult *QueryResult `xml:"queryResponse>result"`
}

type QueryResult struct {
    Done    bool        `xml:"done"`
    Size    int         `xml:"size"`
    Records interface{} `xml:"records"`
}

func (r *Resource) Query(sql string, r interface{}) error {
    t := &QueryEnvelope{Body: &QueryBody{QueryResult: &QueryResult{Records: r}}}
    //执行查询并将结果解组到t中
    return err
}

客户端/主要代码:

type Record struct {
    Id   string `xml:"id"`
    Name string `xml:"name"`
}

r := new([]*Record) // 必须使用new分配内存
err = ent.Query("select id, name from account limit 3", r)
英文:

So you can indeed inject an interface partway through, I was failing it allocate memory for the decoder to work:

Package:

type QueryEnvelope struct {
	XMLName xml.Name   `xml:&quot;http://schemas.xmlsoap.org/soap/envelope/ Envelope&quot;`
	Body    *QueryBody `xml:&quot;http://schemas.xmlsoap.org/soap/envelope/ Body&quot;`
}

type QueryBody struct {
	QueryResult *QueryResult `xml:&quot;queryResponse&gt;result&quot;`
}

type QueryResult struct {
	Done    bool        `xml:&quot;done&quot;`
	Size    int         `xml:&quot;size&quot;`
	Records interface{} `xml:&quot;records&quot;`
}

func (r *Resource) Query(sql string, r interface{}) error {
    t := &amp;QueryEnvelope{Body: &amp;QueryBody{QueryResult: &amp;QueryResult{Records: r}}}
    //do query and unmarshal into t
    return err
}

Client / main:

type Record struct {
	Id   string `xml:&quot;id&quot;`
	Name string `xml:&quot;name&quot;`
}

r := new([]*Record) // must allocate memory using new
err = ent.Query(&quot;select id, name from account limit 3&quot;, r)

huangapple
  • 本文由 发表于 2014年8月22日 22:59:16
  • 转载请务必保留本文链接:https://go.coder-hub.com/25450046.html
匿名

发表评论

匿名网友

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

确定