如何正确解析 XML?

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

How to parse xml correctly

问题

我想创建结构体来表示每种类型的命令。

命令有一个共同的XML部分 - CommandResult。我创建了一个接口Command。我需要SomeCommand实现Command接口,并且可以被解析为XML,同时还需要在CommandResult中实现IsError函数,其他函数由SomeCommand实现。

代码:

type Command interface {
    IsError() bool

    Request(buf *bufio.Writer, params interface{}) error
    ...
}

// 请求的结果
type CommandResult struct {
    Code    int    `xml:"code,attr" json:"code"`
    Message string `xml:"msg" json:"msg"`
}

// 这个Command的函数由CommandResult实现
func (self CommandResult) IsError() bool {
    return true
}


// 一些命令
type SomeCommand struct {
    CommandResult // CommandResult `xml:"response>result" json:"result"`
}

// 这个Command的函数由SomeCommand实现
func (self SomeCommand) Request(buf *bufio.Writer, params interface{}) error {
    return nil
}

// 其他Command的函数也由CommandResult实现

XML:

<epp>
  <response>
    <result code="1000">
      <msg>Command completed successfully</msg>
    </result>
    <trID>
      <svTRID>asd</svTRID>
    </trID>
  </response>
</epp>

期望的结果:

a := SomeCommand
xml.NewDecoder(reader).Decode(&a)
// a.CommandResult.Code = 1000
// a.CommandResult.Message = 'Command completed successfully'

// a实现了Command接口
英文:

I want to create structs = each type of command.

Commands have common part of xml - CommandResult. I created interface Command. I need to SomeCommand implements Command and can to be parsed as xml, also IsError must be realized in CommandResult, other functions must be realized by SomeCommand.

Code:

type Command interface {
    IsError() bool

    Request(buf *bufio.Writer, params interface{}) error
    ...
}

// Result of request
type CommandResult struct {
    Code    int    `xml:&quot;code,attr&quot; json:&quot;code&quot;`
    Message string `xml:&quot;msg&quot; json:&quot;msg&quot;`
}

// this Command&#39;s func is realized by CommandResult 
func (self CommandResult) IsError() bool {
    return true
}


// some command
type SomeCommand struct {
    CommandResult // CommandResult `xml:&quot;response&gt;result&quot; json:&quot;result&quot;`
}

// this Command&#39;s func is realized by SomeCommand 
func (self SomeCommand) Request(buf *bufio.Writer, params interface{}) error {
    return nil
}

// other Command&#39;s functions are realized by CommandResult too

XML:

&lt;epp&gt;
  &lt;response&gt;
    &lt;result code=&quot;1000&quot;&gt;
      &lt;msg&gt;Command completed successfully&lt;/msg&gt;
    &lt;/result&gt;
    &lt;trID&gt;
      &lt;svTRID&gt;asd&lt;/svTRID&gt;
    &lt;/trID&gt;
  &lt;/response&gt;
&lt;/epp&gt;

Expected result:

a := SomeCommand
xml.NewDecoder(reader).Decode(&amp;a)
// a.CommandResult.Code = 1000
// a.CommandResult.Message = &#39;Command completed successfully&#39;

// a implements Command

答案1

得分: 1

我认为嵌入结构中的路径应该是绝对路径,因为所有的“父级”结构都是“子级”的一部分。所以你的代码应该更像是这样的:

type CommandResult struct {
    Code    int    `xml:"response>result>code,attr" json:"code"`
    Message string `xml:"response>result>msg" json:"msg"`
}

但是!我们面临着Go语言的限制。

你不能在链式结构中使用attr。在Github上有一个相关的问题(https://github.com/golang/go/issues/3688),但看起来它不在优先列表中。所以如果我正确理解的话,你的CommandResult声明的最简版本应该是:

type CommandResult struct {
    Result struct {
        Code    int    `xml:"code,attr" json:"code"`
        Message string `xml:"msg" json:"msg"`
    } `xml:"response>result" json:"response"`
}

这不是一个真正的问题,但是如果你决定将Command转换回XML,最好声明它的XMLName。类似这样:

type CommandResult struct {
    XMLName xml.Name `xml:"epp"`
    Result  struct {
        Code    int    `xml:"code,attr" json:"code"`
        Message string `xml:"msg" json:"msg"`
    } `xml:"response>result" json:"response"`
}

因为如果没有它,XML编码器将产生类似<SomeCommand><response>...</response></SomeCommand>的结果。

更新后的完整示例:

package main

import (
    "bufio"
    "encoding/xml"
    "log"
)

type Command interface {
    IsError() bool
    Request(buf *bufio.Writer, params interface{}) error
}

// 请求的结果
type CommandResult struct {
    XMLName xml.Name `xml:"epp"`
    Result  struct {
        Code    int    `xml:"code,attr" json:"code"`
        Message string `xml:"msg" json:"msg"`
    } `xml:"response>result" json:"response"`
}

// 这个Command的函数由CommandResult实现
func (self CommandResult) IsError() bool {
    return true
}

// 一些命令
type SomeCommand struct {
    CommandResult
}

// 这个Command的函数由SomeCommand实现
func (self SomeCommand) Request(buf *bufio.Writer, params interface{}) error {
    return nil
}

type AnotherCommand struct {
    CommandResult
}

func (self AnotherCommand) Request(buf *bufio.Writer, params interface{}) error {
    return nil
}

func main() {
    var c Command

    c = SomeCommand{}
    log.Println(c.IsError())

    c = AnotherCommand{}
    log.Println(c.IsError())
}

希望对你有所帮助!

英文:
  1. I think paths in embedded structure should be absolute as all "parent's" structures are part of the "child". So your

      type CommandResult struct {
          Code    int    `xml:&quot;code,attr&quot; json:&quot;code&quot;`
          Message string `xml:&quot;msg&quot; json:&quot;msg&quot;`
      }
    

    Should be more like

      type CommandResult struct {
          Code    int    `xml:&quot;response&gt;result&gt;code,attr&quot; json:&quot;code&quot;`
          Message string `xml:&quot;response&gt;result&gt;msg&quot; json:&quot;msg&quot;`
      }
    

    BUT! There we are facing Go's limitation.

  2. You can't use attr with chaining. There is issue on Github but looks like it is not in priority list. So if I right understand shortest version of your CommandResult declaration would be:

     type CommandResult struct {
     	Result struct {
     		Code    int    `xml:&quot;code,attr&quot; json:&quot;code&quot;`
     		Message string `xml:&quot;msg&quot; json:&quot;msg&quot;`
     	} `xml:&quot;response&gt;result&quot; json:&quot;response&quot;`
     }
    
  3. Not a real problem but in case if you will decide to convert Command back to XML would be nice to declare its XMLName. Something like

     type CommandResult struct {
     	XMLName xml.Name `xml:&quot;epp&quot;`
     	Result  struct {
     		Code    int    `xml:&quot;code,attr&quot; json:&quot;code&quot;`
     		Message string `xml:&quot;msg&quot; json:&quot;msg&quot;`
     	} `xml:&quot;response&gt;result&quot; json:&quot;response&quot;`
     }
    

    Because without it XML encoder will produce something like &lt;SomeCommand&gt;&lt;response&gt;...&lt;/response&gt;&lt;/SomeCommand&gt;

Update with full example

package main

import (
	&quot;bufio&quot;
	&quot;encoding/xml&quot;
	&quot;log&quot;
)

type Command interface {
	IsError() bool

	Request(buf *bufio.Writer, params interface{}) error
}

// Result of request
type CommandResult struct {
	XMLName xml.Name `xml:&quot;epp&quot;`
	Result  struct {
		Code    int    `xml:&quot;code,attr&quot; json:&quot;code&quot;`
		Message string `xml:&quot;msg&quot; json:&quot;msg&quot;`
	} `xml:&quot;response&gt;result&quot; json:&quot;response&quot;`
}

// this Command&#39;s func is realized by CommandResult
func (self CommandResult) IsError() bool {
	return true
}

// some command
type SomeCommand struct {
	CommandResult
}

// this Command&#39;s func is realized by SomeCommand
func (self SomeCommand) Request(buf *bufio.Writer, params interface{}) error {
	return nil
}

type AnotherCommand struct {
	CommandResult
}

func (self AnotherCommand) Request(buf *bufio.Writer, params interface{}) error {
	return nil
}

func main() {
	var c Command

	c = SomeCommand{}
	log.Println(c.IsError())

	c = AnotherCommand{}
	log.Println(c.IsError())
}

huangapple
  • 本文由 发表于 2016年2月3日 21:54:32
  • 转载请务必保留本文链接:https://go.coder-hub.com/35178998.html
匿名

发表评论

匿名网友

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

确定