006-golang中XML文件的处理

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文档有详细的说明)

  1. 如果struct的一个字段是string或者[]byte类型且它的tag含有”,innerxml”,Unmarshal会将此字段所对应的元素内所有内嵌的原始xml累加到此字段上。
  2. 如果struct中有一个叫做XMLName且类型为xml.Name的字段,Unmarshal会保存对应的元素的名字到该字段。比如,上面的例子,在Person结构中加上该字段XMLName xml.Name,则结果会是:{[{{ person} polaris 28 无业游民 {[编程 下棋]}} {{ person} studygolang 27 码农 {[编程 下棋]}}]}。可见,该字段是用来映射XML元素的,在生成XML时比较有用。注意,XMLName和类型xml.Name必须是这样,不能改为XmlName。
  3. 如果XMLName字段有tag,且tag的形式是:”name”或”namespace-URL name”,则相应的XML元素必须是这个名字(命名空间可选),否则Unmarshal会返回错误。可以通过在上面的例子中,修改Person的XMLName xml.Name xml:”myperson”试试,会报错:expected element typebut have
  4. 如果某个XML元素有一个属性,它的名字和struct中某个字段匹配(大小写都得匹配),并且该字段的tag包含”,attr”,或者元素的名字显示的被写在了tag中(”name,attr”),这时,Unmarshal会将该属性赋值给该字段。如上面的Name和Age
  5. 如果XML元素包含字符数据(character

data),那么,字符数据会被累加到struct中第一个有tag为”,chardata”的字段。struct字段的类型可以是string或[]byte。如果没有这样的字段,字符数据会被丢弃。如上面的Interests可以再定义一个类型Interest:
type Interest struct {
Inter string xml:”,chardata”
}
Interests 中相应的改为:Interest []Interest
当然这个例子中这种方式有些啰嗦。

  1. 如果某个XML元素包含一条或者多条注释,那么这些注释将被累加到第一个含有”,comments” tag的字段上,这个字段的类型可以是[]byte或string,如果没有这样的字段存在,那么注释将会被丢弃。
  2. 如果某个XML元素的子元素的名字和 “a”或 “a>b>c”这种格式的tag的前缀匹配,Unmarshal会沿着XML结构向下寻找这样名字的元素,然后将最里面的元素映射到struct的字段上。以”>”开始的tag和字段后面跟上”>”是等价的。从这知道,上面例子中关于Interests的解析可以更简单,即不需要Interest结构类型
  3. 如果某XML元素的子元素的名字和某个struct的XMLName字段的tag匹配,且该struct的字段没有定义以上规则的tag,Unmarshal会映射该子元素到该struct的字段上。
  4. 如果某个XML元素的子元素的名字和一个没有任何tag的字段匹配,则Unmarshal会映射这个子元素到那个字段上。比如最开始没有使用tag的例子,使用的就是这条规则。
  5. 如果某个XML元素的子元素根据上面的规则都没有匹配到任何字段,然而,该struct有字段带有”,any”的tag,则Unmarshal会映射该子元素到该字段上。
  6. 一个非指针的匿名struct字段会被这样处理:该字段的值是外部struct的一部分

12 . 如果一个struct字段的tag定义为”-“,则Unmarshal不会给它赋值

三.参考链接

  1. Go语言关于XML的读取与生成

  2. 标准库—XML处理(一)

  3. go语言解析带命名空间的xml

  4. http://www.jianshu.com/p/7ac5db1d6b70

  5. golang标准库encodeing/xml文档

    原文作者:夏大王2019
    原文地址: https://www.jianshu.com/p/d7157093c246
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞