XML in Go
在Go的标准包里包含一个xml包来处理xml文件“encoding/xml", 我们可以载入它来对xml进行操作。
首先我们先建立一个xml(Person.xml)
<Person> <FullName>Grace R. Emlin</FullName> <Company>Example Inc.</Company> <Email where="home"> <Addr>gre@example.com</Addr> </Email> <Email where='work'> <Addr>gre@work.com</Addr> </Email> <Group> <Value>Friends</Value> <Value>Squash</Value> </Group> <City>Hanga Roa</City> <State>Easter Island</State> </Person>
a. xml对应struct的构造
接下来我们需要给这个xml文件建立一个与我们解析后对应的xml对象,而这个对象我们可以用struct来构造。这个对象的结构是不用和xml的原来的结构一直,可以根据自己的需求
做自定义。我一开始对这个不是很适应觉得很麻烦,但后来发现可以相当灵活的控制xml对象。先给出一个定义,然后再一句一句的解释。
1 type Email struct { 2 Where string `xml:"where,attr"` 3 Addr string 4 } 5 6 type Address struct { 7 City, State string 8 } 9 10 type Result struct { 11 XMLName xml.Name `xml:"Person"` 12 Name string `xml:"FullName"` 13 Phone string 14 Email []Email15 Groups []string `xml:"Group>Value"` 16 Address 17 }
Line 1-4: 我们把<Email>节点作为一个struct,它包含了2个字段分别是Where,Addr。
Where: 读取的是Email节点的属性值,在字段定义的最后用`xml:"where,attr"`表示。
Addr:读取的是Email节点下的<Addr>的值,需要注意的是字段的名字要和xml文件的里的字段大小写一致。
这样可以不需要再声明一个Addr的struct就可以直接映射了。
Line 6-8: 这里我们定义了一个Address的struct,但是在xml里并没有对应的节点。但是Address里的值是把xml里的City和State组合在一起了。
Line 10-17: 定义了一个总的struct来描述整个xml。
XMLName:它的字段的类型是xml.Name,说明了xml的名字
Name:在xml里并没有Name的节点,但是在后面`xml:"FullName"`我们指明了它是映射到FullName节点上的值
Phone:更进一步了,在整个xml里没有Phone节点,而且我们也没用给出任何映射。这样我们把xml读到内存中时就对它进行了一些“改造”,很灵活吧。
Email:需要注意的是这里的类型用的Email,正好是我们先前定义的类型,而且是[]说明会有多个。
Groups:这个也很有意思,">"说明的是子元素,这里指的是Group节点下的子节点Value。
Address:直接用了我们上面定义的Address,没有类型指定。
Note:另外需要说明的是struct里的字段必须是大写时才能被解析。
对于xml属性定义的具体规则可以在这里查询:http://golang.org/pkg/encoding/xml/
KO。到这里我们的准备已经做好了剩下的就是来解析我们的xml。至于解析xml我目前知道两种:xml.Unmarshal() 和 decoder
先说第一种,xml.Unmarshal()是xml包里提供的方法:
var v Result
xmlFile, err := ioutil.ReadFile("Person.xml") if err != nil { fmt.Println("Error opening file: ", err) return } err1 := xml.Unmarshal(xmlFile, &v) if err1 != nil { fmt.Printf("error: %v", err) return }
Note:需要注意的是这里需要传入指针&v
第二种情况我们用流的形式来解析,用这种方式我们可以很容易的处理大数据的xml。
xmlFile, err := os.Open("Person.xml") if err != nil { fmt.Println("Error opening file: ", err) return } defer xmlFile.Close() decoder := xml.NewDecoder(xmlFile) for { t, _ := decoder.Token() if t == nil { break } switch se := t.(type) { case xml.StartElement: if se.Name.Local == "Person" { var d Result decoder.DecodeElement(&d, &se) fmt.Println(d) } } }
Note:t.(type)的用法只能在switch的类型遍历中使用,具体可以参考:http://golang.org/doc/effective_go.html#switch
Refference:
1. http://golang.org/pkg/encoding/xml
2. http://blog.davidsingleton.org/parsing-huge-xml-files-with-go/