go解析未知结构XML

在最近的工作中,要把一个C的程序需要用Go来改写。在C中使用minixml对xml文件进行解析并不需要预先知道xml文件的结构,只需要沿着树往下找,看是否能够找到所需的元素或者属性,这在用户输入错误的xml文件时候也能进行错误处理,告知错误。
在Go中,网上搜到的xml的解析大部分都是将已知文件映射到struct,使用xml包的Unmarshall来完成。而对于未知结构的xml文件,看到有如下几种处理方式
1. 对于部分结构已知的xml文件,并且也只需要该部分的数据,可以定义struct只包含该部分元素。实际上struct并不需要完全对应xml文件的所有内容,只需要包含所需数据部分即可。
2. 参考http://blog.csdn.net/qu_tao/article/details/77678691,marshal可以处理指针指向的值, 若指针为nil, 则不会写入到xml中.
我们可以在定义解析的struct结构时, 将元素的类型定义为指针类型, 若解析时不存在该元素, 则在marshal时不会生成空的元素.
[Crayon lang="go"]
type ObjectInfo struct {
// attr
ObjectType string `xml:"ObjectType,attr"`
Id string `xml:"Id,attr"`
Name string `xml:"Name,attr"`
CreationDate string //`xml:"CreationDate"` // struct tag is not necessary
// tag
Tag []TagInfo `xml:"TagCollection>Tag"`
// ColorValues
RefSpecData *ReflectanceSpectrumInfo `xml:"ColorValues>ReflectanceSpectrum"`
ColorLabData *ColorCIELabInfo `xml:"ColorValues>ColorCIELab"`
ColorDensityData *ColorDensityInfo `xml:"ColorValues>ColorDensity"`
//ColorValues PrintDataInterface `xml:",innerxml"`

// DeviceColorValues
ColorCMYKPlusNData *ColorCMYKPlusNInfo `xml:"DeviceColorValues>ColorCMYKPlusN"`
ColorCMYKData *ColorCMYKInfo `xml:"DeviceColorValues>ColorCMYK"`
//DeviceColorValues PrintDataInterface `xml:",innerxml"`
}
[/Crayon]
上面结构是在解析CxF定义的结构, 扫描后生成的数据类型是不确定的, 但又是固定的几组结构, 指针类型能很好的解决这个问题.
3.对于整个文件未知或者所需部分未知的情况,参考https://gocn.io/question/291的回答,可以创建一个xml.Decoder,然后用Token方法去遍历
[Crayon lang="go"]
dec := xml.NewDecoder(bytes.NewBuffer([]byte(xmlText)))
for {
token, err := dec.Token()
if err != nil {
if err == io.EOF {
break
}
panic(err)
}
switch element := token.(type) {
case xml.StartElement:
// ...
case xml.EndElement:
// ...
case xml.CharData:
// ...
case xml.Comment:
// ...
case xml.Directive:
// ...
case xml.ProcInst:
// ...
}
}
[/Crayon]
也可以参考教程https://www.ctolib.com/docs/sfile/the-way-to-go_ZH_CN/eBook/12.10.html
和 JSON 的方式一样,XML 数据可以序列化为结构,或者从结构反序列化为 XML 数据;这些可以在例子 15.8(twitter_status.go)中看到。
encoding/xml 包实现了一个简单的 XML 解析器(SAX),用来解析 XML 数据内容。下面的例子说明如何使用解析器:
示例 12.17 xml.go:
[Crayon lang="go"]
// xml.go
package main

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

var t, token xml.Token
var err error

func main() {
input := "LauraLynn"
inputReader := strings.NewReader(input)
p := xml.NewDecoder(inputReader)

for t, err = p.Token(); err == nil; t, err = p.Token() {
switch token := t.(type) {
case xml.StartElement:
name := token.Name.Local
fmt.Printf("Token name: %s\n", name)
for _, attr := range token.Attr {
attrName := attr.Name.Local
attrValue := attr.Value
fmt.Printf("An attribute is: %s %s\n", attrName, attrValue)
// ...
}
case xml.EndElement:
fmt.Println("End of token")
case xml.CharData:
content := string([]byte(token))
fmt.Printf("This is the content: %v\n", content)
// ...
default:
// ...
}
}
}
[/Crayon]
输出:
[Crayon lang="go"]
Token name: Person
Token name: FirstName
This is the content: Laura
End of token
Token name: LastName
This is the content: Lynn
End of token
End of token
[/Crayon]
包中定义了若干 XML 标签类型:StartElement,Chardata(这是从开始标签到结束标签之间的实际文本),EndElement,Comment,Directive 或 ProcInst。
包中同样定义了一个结构解析器:NewParser 方法持有一个 io.Reader(这里具体类型是 strings.NewReader)并生成一个解析器类型的对象。还有一个 Token() 方法返回输入流里的下一个 XML token。在输入流的结尾处,会返回(nil,io.EOF)
XML 文本被循环处理直到 Token() 返回一个错误,因为已经到达文件尾部,再没有内容可供处理了。通过一个 type-switch 可以根据一些 XML 标签进一步处理。Chardata 中的内容只是一个 []byte,通过字符串转换让其变得可读性强一些。

可以从encoding/xml文档中看到token有五种type:StartElement, EndElement, CharData, Comment, ProcInst和Directive。StartElement是xml的起始标记,包括名字和一组属性,EndElement是终止标签,包括名字,CharData是数据内容的原始文本(raw data),Comment为形如的注释,返回内容,不包括两边的符号,ProcInst是形如的指令,如,包括目标和一组指令,Directive是形如的PCDATA、CDATA,返回内容,不包括两边的符号。

喜欢 0

这篇文章还没有评论

发表评论