在Go语言中解组异构XML元素列表

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

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 &lt;val&gt; 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 &amp;&amp; len(se.Attr) &gt; 0 {
			entries = append(entries, Entry{
				Name:  se.Name.Local,
				Attr:  se.Attr[0].Name.Local,
				Value: se.Attr[0].Value,
			})
		}
	}

	fmt.Printf(&quot;%+v\n&quot;, entries)
}

const src = `&lt;val&gt;
&lt;alpha β=&#39;γ&#39;/&gt;
&lt;α δ=&#39;ε&#39;/&gt;
&lt;x y=&#39;z&#39;/&gt;
&lt;/val&gt;`

Output (try it on the Go Playground):

[{Name:alpha Attr:β Value:γ} {Name:α Attr:δ Value:ε} {Name:x Attr:y Value:z}]

huangapple
  • 本文由 发表于 2016年12月6日 12:44:54
  • 转载请务必保留本文链接:https://go.coder-hub.com/40987994.html
匿名

发表评论

匿名网友

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

确定