golang保留顺序中的混合XML解码

Mir*_*lav 6 xml go slice xml-deserialization

我需要从XML中提取商品,但要考虑节点顺序:

<items>
  <offer/>
  <product>
    <offer/>
    <offer/>
  </product>
  <offer/>
  <offer/>
</items>

以下结构将解码值,但会解码为两个不同的切片,这将导致原始顺序丢失:

type Offers struct {
    Offers   []offer `xml:"items>offer"`
    Products []offer `xml:"items>product>offer"`
}

有任何想法吗?

mik*_*iku 8

一种方法是覆盖该UnmarshalXML方法.假设我们的输入如下:

<doc>
    <head>My Title</head>
    <p>A first paragraph.</p>
    <p>A second one.</p>
</doc>
Run Code Online (Sandbox Code Playgroud)

我们希望反序列化文档并保留头部和段落的顺序.对于订单,我们需要一个切片.以适应headp,我们就需要一个接口.我们可以像这样定义我们的文档:

type Document struct {
    XMLName  xml.Name `xml:"doc"`
    Contents []Mixed  `xml:",any"`
}
Run Code Online (Sandbox Code Playgroud)

,any注释将收集所有元素插入Contents.它是一种Mixed类型,我们需要将其定义为一种类型:

type Mixed struct {
    Type  string      // just keep "head" or "p" in here
    Value interface{} // keep the value, we could use string here, too
}
Run Code Online (Sandbox Code Playgroud)

我们需要更多地控制反序列化过程,因此我们通过实现变成Mixed了一个.我们根据start元素的名称决定代码路径,例如或.在这里,我们只用一些值填充我们的结构,但你基本上可以在这里做任何事情:xml.UnmashalerUnmarshalXMLheadpMixed

func (m *Mixed) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {
    switch start.Name.Local {
    case "head", "p":
        var e string
        if err := d.DecodeElement(&e, &start); err != nil {
            return err
        }
        m.Value = e
        m.Type = start.Name.Local
    default:
        return fmt.Errorf("unknown element: %s", start)
    }
    return nil
}
Run Code Online (Sandbox Code Playgroud)

总而言之,上述结构的使用可能如下所示:

func main() {
    s := `
    <doc>
        <head>My Title</head>
        <p>A first paragraph.</p>
        <p>A second one.</p>
    </doc>
    `

    var doc Document
    if err := xml.Unmarshal([]byte(s), &doc); err != nil {
        log.Fatal(err)
    }
    fmt.Printf("#%v", doc)
}   
Run Code Online (Sandbox Code Playgroud)

哪个会打印.

#{{ doc} [{head My Title} {p A first paragraph.} {p A second one.}]}
Run Code Online (Sandbox Code Playgroud)

我们保留了订单并保留了一些类型信息.Mixed您可以使用许多不同的类型进行反序列化,而不是单一类型.这种方法的成本是您的容器 - 这里Contents是文档的字段 - 是一个接口.要执行特定于元素的任何操作,您需要一个类型断言或一些辅助方法.

完整的游戏代码:https://play.golang.org/p/fzsUPPS7py