golang中XML文件的处理
一.概述
在golang中 包”encoding/xml” 提供了对xml文件的解析。
二.简单解析举例
有两种常见方式对其解析,
1.直接访问
举个栗子
package main
import (
"fmt"
"io/ioutil"
"errors"
"os"
"log"
"flag"
"os/exec"
"regexp"
"strings"
"encoding/xml"
"bytes"
)
manifest := projectDir + "/AndroidManifest.xml"
content, err := ioutil.ReadFile(manifest)
if err != nil {
log.Fatal(err)
}
//inputReader := strings.NewReader(content)
//decoder := xml.NewDecoder(inputReader)
decoder := xml.NewDecoder(bytes.NewBuffer(content))
for t, err := decoder.Token(); err == nil; t, err = decoder.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.Printf("Token of '%s' end\n", token.Name.Local)
// 处理字符数据(这里就是元素的文本)
case xml.CharData:
content := string([]byte(token))
fmt.Printf("This is the content: %v\n", content)
default:
// ...
}
}
fmt.Printf("xml success!\n")
2.xml和Go中的struct相互转换
缺点: 兼容性不好,而且要定义很多struct
有点,转化后的数据,看起来清晰
举个栗子
用栗子说话
package main
//import "github.com/gin-gonic/gin"
import (
//"encoding/json"
"fmt"
"io/ioutil"
//"net/http"
"log"
"encoding/xml"
"os"
)
const Header string = `<?xml version="1.0" encoding="utf-8" standalone="no"?>` + "\n"
type manifest struct {
Xmlns xml.Attr `xml:"android,attr"`
InstallLocation xml.Attr `xml:"installLocation,attr"`
Package xml.Attr `xml:"package,attr"`
PlatformBuildVersionCode xml.Attr `xml:"platformBuildVersionCode,attr"`
PlatformBuildVersionName xml.Attr `xml:"platformBuildVersionName,attr"`
SupportsScreens SupportsScreens `xml:"supports-screens"`
}
type SupportsScreens struct {
AnyDensity xml.Attr `xml:"anyDensity,attr"`
LargeScreens xml.Attr `xml:"largeScreens,attr"`
NormalScreens xml.Attr `xml:"normalScreens,attr"`
SmallScreens xml.Attr `xml:"smallScreens,attr"`
XlargeScreens xml.Attr `xml:"xlargeScreens,attr"`
}
func NewManifest(filename string)(manifest) {
content, err := ioutil.ReadFile(filename)
if err != nil {
log.Fatal(err)
}
var result manifest
err = xml.Unmarshal(content, &result)
if err != nil {
log.Fatal(err)
}
//log.Println(result)
//log.Println(result.Persons[0].Name)
return result
}
func main() {
//xml.Attr
result := NewManifest("project/name/AndroidManifest.xml")
fmt.Printf("-->%+v\n",result)
xmlOutPut, outPutErr := xml.MarshalIndent(result, "", "")
if outPutErr == nil {
//加入XML头
headerBytes := []byte(xml.Header)
//拼接XML头和实际XML内容
xmlOutPutData := append(headerBytes, xmlOutPut...)
//写入文件
ioutil.WriteFile("test.xml", xmlOutPutData, os.ModeAppend)
fmt.Println("OK~")
} else {
fmt.Println(outPutErr)
}
}
3.XML到Go中struct的转换规则说明。
(标准库encoding/xml文档有详细的说明)
- 如果struct的一个字段是string或者[]byte类型且它的tag含有”,innerxml”,Unmarshal会将此字段所对应的元素内所有内嵌的原始xml累加到此字段上。
- 如果struct中有一个叫做XMLName且类型为xml.Name的字段,Unmarshal会保存对应的元素的名字到该字段。比如,上面的例子,在Person结构中加上该字段XMLName xml.Name,则结果会是:{[{{ person} polaris 28 无业游民 {[编程 下棋]}} {{ person} studygolang 27 码农 {[编程 下棋]}}]}。可见,该字段是用来映射XML元素的,在生成XML时比较有用。注意,XMLName和类型xml.Name必须是这样,不能改为XmlName。
- 如果XMLName字段有tag,且tag的形式是:”name”或”namespace-URL name”,则相应的XML元素必须是这个名字(命名空间可选),否则Unmarshal会返回错误。可以通过在上面的例子中,修改Person的XMLName xml.Name
xml:”myperson”
试试,会报错:expected element typebut have - 如果某个XML元素有一个属性,它的名字和struct中某个字段匹配(大小写都得匹配),并且该字段的tag包含”,attr”,或者元素的名字显示的被写在了tag中(”name,attr”),这时,Unmarshal会将该属性赋值给该字段。如上面的Name和Age
- 如果XML元素包含字符数据(character
data),那么,字符数据会被累加到struct中第一个有tag为”,chardata”的字段。struct字段的类型可以是string或[]byte。如果没有这样的字段,字符数据会被丢弃。如上面的Interests可以再定义一个类型Interest:
type Interest struct {
Inter string xml:”,chardata”
}
Interests 中相应的改为:Interest []Interest
当然这个例子中这种方式有些啰嗦。
- 如果某个XML元素包含一条或者多条注释,那么这些注释将被累加到第一个含有”,comments” tag的字段上,这个字段的类型可以是[]byte或string,如果没有这样的字段存在,那么注释将会被丢弃。
- 如果某个XML元素的子元素的名字和 “a”或 “a>b>c”这种格式的tag的前缀匹配,Unmarshal会沿着XML结构向下寻找这样名字的元素,然后将最里面的元素映射到struct的字段上。以”>”开始的tag和字段后面跟上”>”是等价的。从这知道,上面例子中关于Interests的解析可以更简单,即不需要Interest结构类型
- 如果某XML元素的子元素的名字和某个struct的XMLName字段的tag匹配,且该struct的字段没有定义以上规则的tag,Unmarshal会映射该子元素到该struct的字段上。
- 如果某个XML元素的子元素的名字和一个没有任何tag的字段匹配,则Unmarshal会映射这个子元素到那个字段上。比如最开始没有使用tag的例子,使用的就是这条规则。
- 如果某个XML元素的子元素根据上面的规则都没有匹配到任何字段,然而,该struct有字段带有”,any”的tag,则Unmarshal会映射该子元素到该字段上。
- 一个非指针的匿名struct字段会被这样处理:该字段的值是外部struct的一部分
12 . 如果一个struct字段的tag定义为”-“,则Unmarshal不会给它赋值