英文:
Unmarshalling heterogeneous list of XML elements in Go
问题
我有一个像这样的XML文档:
<val>
<alpha β='γ'/>
<α δ='ε'/>
(一堆类似上面的,顺序随机)
</val>
换句话说,这是一个异构列表。我想使用Go的encoding/xml
包对其进行解组。我该如何做到这一点?
英文:
I have an XML document like this:
<val>
<alpha β='γ'/>
<α δ='ε'/>
(a whole bunch of the above, in random order)
</val>
in other words, a heterogeneous list. I would like to unmarshal it using the Go encoding/xml
package. How can I do this?
答案1
得分: 3
你可以在Go中无法对这样的XML文档建模,也无法将其解组为interface{}
,因此我建议使用事件驱动的解析。
事件驱动的解析意味着在解析(XML文档的标记)时,你会接收到诸如“遇到起始元素”或“遇到结束元素”(当然还有元素的详细信息)的事件,这些事件控制程序的流程(根据它们进行分支和/或更改内部状态)。
以下示例将展示这个原理。它不处理更复杂的XML,因为我希望示例尽可能简短,但是这种技术可以用于解析任何XML文档。
使用xml.NewDecoder()
创建一个xml.Decoder
,并通过反复调用Decoder.Token()
来解析XML的内容(在循环中)。
<val>
内部的元素将被收集到类型为[]Entry
的切片中:
type Entry struct {
Name string
Attr string
Value string
}
func main() {
decoder := xml.NewDecoder(strings.NewReader(src))
entries := []Entry{}
for {
t, err := decoder.Token()
if err != nil {
if err != io.EOF {
fmt.Println(err)
}
break
}
if se, ok := t.(xml.StartElement); ok && len(se.Attr) > 0 {
entries = append(entries, Entry{
Name: se.Name.Local,
Attr: se.Attr[0].Name.Local,
Value: se.Attr[0].Value,
})
}
}
fmt.Printf("%+v\n", entries)
}
const src = `<val>
<alpha β='γ'/>
<α δ='ε'/>
<x y='z'/>
</val>`
输出结果(在Go Playground上尝试):
[{Name:alpha Attr:β Value:γ} {Name:α Attr:δ Value:ε} {Name:x Attr:y Value:z}]
英文:
You can't model such XML documents in Go, and you can't unmarshal into interace{}
, so I would suggest event-driven parsing for this.
This event-driven parsing means when you are parsing (the tokens of) the XML document, you receive events like "start element encountered", or "end element encountered" (with details of the element of course), and these events control the flow of your program (branch and/or change internal state based on them).
The following example will show you the principle of this. It does not handle more complex XMLs because I wanted the example to be short, but this technique can be used to parse any XML documents.
Create an xml.Decoder
using xml.NewDecoder()
, and parse the content of the XML by calling Decoder.Token()
repeatedly (in a loop).
The elements inside <val>
will be collected in a slice of type []Entry
:
type Entry struct {
Name string
Attr string
Value string
}
func main() {
decoder := xml.NewDecoder(strings.NewReader(src))
entries := []Entry{}
for {
t, err := decoder.Token()
if err != nil {
if err != io.EOF {
fmt.Println(err)
}
break
}
if se, ok := t.(xml.StartElement); ok && len(se.Attr) > 0 {
entries = append(entries, Entry{
Name: se.Name.Local,
Attr: se.Attr[0].Name.Local,
Value: se.Attr[0].Value,
})
}
}
fmt.Printf("%+v\n", entries)
}
const src = `<val>
<alpha β='γ'/>
<α δ='ε'/>
<x y='z'/>
</val>`
Output (try it on the Go Playground):
[{Name:alpha Attr:β Value:γ} {Name:α Attr:δ Value:ε} {Name:x Attr:y Value:z}]
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论