在Go中解析XML时处理命名空间

时间:2013-01-03 19:33:37

标签: xml go xml-namespaces

我正在尝试解析Go中的XML:

package main

import (
    "encoding/xml"
    "fmt"
)

type XML struct {
    Foo string `xml:"foo"`
}

func main() {
    rawXML := []byte(`
<xml>
  <foo>A</foo>
  <ns:foo>B</ns:foo>
</xml>`)

    x := new(XML)
    xml.Unmarshal(rawXML, x)
    fmt.Printf("foo: %s\n", x.Foo)
}

输出:

foo: B

虽然我预计会产生:

foo: A

如何获取第一个foo标记的内容(即没有名称空间的标记)?

3 个答案:

答案 0 :(得分:5)

我认为xml解码器不能指定一个元素应该没有带有struct标签的命名空间。但我知道它可以为您检索有关命名空间的信息,然后您可以在之后处理数据以获得相同的结果:

package main

import (
    "encoding/xml"
    "fmt"
)

type Foo struct {
    XMLName xml.Name
    Data string `xml:",chardata"`
}

type XML struct {
    Foo []Foo `xml:"foo"`
}

func main() {
    rawXML := []byte(`
<xml>
  <foo>A</foo>
  <ns:foo>B</ns:foo>
</xml>`)

    x := new(XML)
    xml.Unmarshal(rawXML, x)
    //fmt.Printf("foo: %#v\n", x)
    for _, el := range x.Foo {
       if el.XMLName.Space == "" {
          fmt.Printf("non namespaced foo %q", el.Data)
      }
    }
}

http://play.golang.org/p/aDEFPmHPc0

答案 1 :(得分:4)

xml文档中有两个串联值。您的结构中只有一个值的空间。 xml解析器正在解析第一个,然后用第二个覆盖它。

将Foo更改为结构中的切片,然后您将获得两个值。

http://play.golang.org/p/BRgsuMQ7rK

package main

import (
    "encoding/xml"
    "fmt"
)

type XML struct {
    Foo []string `xml:"foo"`
}

func main() {
    rawXML := []byte(`
<xml>
  <foo>A</foo>
  <ns:foo>B</ns:foo>
</xml>`)

    x := new(XML)
    xml.Unmarshal(rawXML, x)
    fmt.Printf("foo: %s\n", x.Foo[0])
    fmt.Printf("both: %v\n", x.Foo)
}

答案 2 :(得分:0)

xml:"foo"选择器语法采用了可选的命名空间xml:"ns foo",但问题是它不支持选择无命名空间的方法。

一种解决方法是使用xml.Decoder.DefaultSpace来简单地为未命名的标签分配一个名称空间,您现在可以使用xml:"<ns> <tag>"语法选择该标签:

https://play.golang.org/p/1UggvqLFT9x

import (
    "encoding/xml"
    "strings"
    "fmt"
)

type Doc struct {
    Foo string `xml:"_ foo"` // <-- <foo> will now be <_:foo>
    NsFoo string `xml:"ns foo"`
}

var input = `<xml>
  <foo>A</foo>
  <ns:foo>B</ns:foo>
</xml>`

func main() {
    decoder := xml.NewDecoder(strings.NewReader(input))
    decoder.DefaultSpace = "_"

    doc := &Doc{}
    decoder.Decode(doc)

    fmt.Printf("<foo>: %#v\n", doc.Foo)
    fmt.Printf("<ns:foo>: %#v\n", doc.NsFoo)

}

打印:

<foo>: A
<ns:foo>: B