加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 大数据 > 正文

Golang xml 使用

发布时间:2020-12-16 18:09:36 所属栏目:大数据 来源:网络整理
导读:解析和读取规则 golang对xml的解析和读取是通过stuct和refect实现的,对于struct中的tag以什么方式对应到xml的元素上,golang的文档中做了如下描述: 结构体中的XMLName字段或者类型为xml.Name的字段,会被删除.使用此字段tag上定义的属性进行解析 结构体tag中”

解析和读取规则

golang对xml的解析和读取是通过stuct和refect实现的,对于struct中的tag以什么方式对应到xml的元素上,golang的文档中做了如下描述:

  1. 结构体中的XMLName字段或者类型为xml.Name的字段,会被删除.使用此字段tag上定义的属性进行解析
  2. 结构体tag中”-” 在解析过程中会忽略结构体中的这个字段
  3. 结构体tag中”name,attr” 使用name作输出为xml属性,对应字段值作为属性值
  4. 结构体tag中”,attr” 使用字段名作为xml属性,字段值作为xml属性值
  5. 结构体tag中”,chardata” 不作为xml的节点输出,把该字段对应的值作为字符输出
  6. 结构体tag中 “,innerxml” 如果结构体改字段是基本类型如:string,int等,和”,chardata”输出无区别,如果是一个结构体,输出值会是一个完整的xml结构
  7. 结构体tag中 “,comment” 输出xml中的注释
  8. 结构体tag中”omitempty” 该字段是go中的空值:false,空指针,空接口,任何长度为0的切片,数组,字符串和map. 都会被忽略
  9. 结构体中不包含tag 会以改字段作为xml属性名称,值作为xml属性值

举个例子来说明一下上面描述的规则

xml 解析

package main

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

type TNote struct {
    Lang    string `xml:"lang,attr"`
    Content string `xml:",innerxml"`
}

type TFile struct {
    XMLName  struct{} `xml:"file"`
    FileName string   `xml:"name,attr"`
    Size     string   `xml:"size,attr"`
}

type Release struct {
    XMLName   struct{} `xml:"release"`
    Version   string   `xml:"version,attr"`
    TimeStamp string   `xml:",attr"`
    Lang      string   `xml:"-"`
    Skin      string   `xml:",chardata"`
    Site      string   `xml:",omitempty"`
    File      []TFile  `xml:",innerxml"`
    CnNotes   TNote    `xml:"cnnote"`
    EnNotes   TNote    `xml:"ennote"`
    Comment   string   `xml:",comment"`
}

func main(){
    release := Release{Version:   "1.0.0.0",TimeStamp: time.Now().String(),Lang:      "zh-cn",Site:      "",File: []TFile{TFile{FileName: "/deploy/package_1.zip",Size: "50"},TFile{FileName: "/deploy/package_2.zip",Size: "60"},},Skin:    "blue",Comment: "this is a test for xml parser.",}

    v,err := xml.MarshalIndent(release,""," ")
    if err != nil {
        fmt.Println("marshal xml value error,error msg:%s",err.Error())
    }

    fmt.Println("marshal xml value",string(v))

}

上面程序编译运行后的结果

marshal xml value
 <release version="1.0.0.0" TimeStamp="2017-07-13 22:53:13.275704037 +0800 CST">
         blue
         <file name="/deploy/package_1.zip" size="50"></file>
         <file name="/deploy/package_2.zip" size="60"></file>
         <cnnote lang=""></cnnote>
         <ennote lang=""></ennote>
         <!--this is a test for xml parser.-->
</release>

没有被输出的值:

从运行结果我们很明显的看到struct中的Lang和Site属性没有被解析到对应的xml中,因为Lang的tag中设置了”-“,被直接忽略掉,而Site的tag被设置成”,omitempty”,它的值是go中的空值(空string)

name,attr和,attr的区别:

这两个tag在解析的时候有一个细微的差别,可能细心的人已经发现了.tag被设置成”name,attr”的时候,解析出来的xml对应的属性值为name指定的字符串,例如本例中的version=”1.0.0.0”;tag被设置成”,attr”时会议struct的字段作为xml的属性名称,本例中的TimeStamp=”2017-07-13 22:53:13.275704037 +0800 CST”

上面的例子已经基本上能解决我们平时中的大部分问题,有时候我们也会碰到比较corner case.例如上面我们把go中的string,slice,struct解析到xml的结构中,那么map能解析到xml中吗?像上面例子中的cnnote和ennote如果我们只想在xml的输出结果中只显示其中一个,我们该怎么做.


自定义xml解析

这种方式我们需要实现Golang提供的Marshaler接口

type Marshaler interface { MarshalXML(e *Encoder,start StartElement) error }

接口的第一个参数接收一个或多个xml 元素,多个元素一般是array或者slice类型,例如上面那个例子的File元素

第二个参数是xml的开始节点也可视为根节点,例如上面的release

func (r Release) MarshalXML(e *xml.Encoder,start xml.StartElement) error{
    //构建xml 输出头部
    p := xml.ProcInst{"xml",[]byte(`version="1.0" encoding="UTF-8"`)}
    e.EncodeToken(p)

    e.EncodeToken(start)

    //按照语言类型输出对应的节点的信息
    if (en) {
        e.Encode(r.EnNotes)
    }else{
        e.Encode(r.CnNotes)
    }
    //xml 根节点结束标记
    e.EncodeToken(start.End())

    e.Flush()

    return nil
}

通过对MarshalXML这个接口的实现,我们可以按照我们的需要对xml节点进行自定义输出.同时从上面的实现看,这个接口的实现基本思想是把xml的元素一个个的分开解析,只是把不需要显示的元素我们不对其使用e.Encode()方法.

Map类型的xml解析

map类型如何解析对应到xml节点,上面我们已经能够把基本的struct对应到xml上,并且可以自定义解析的xml.对于map解析,我们首先可以把map转化成一个能够解析成xml的struct然后在解析.来看一个列子

type Map map[string]string

type Person struct {
    XMLName xml.Name `xml:"person"` 
    Name   string `xml:"name,attr"`
    Age  string `xml:"age,attr"`
}

m := map[string]string{
    Name:"Chris",Age:"25"    
}

p := make([]Person, 0)

for k,v := range m {
    p = append(p,Person{Name:k,Aage:v})
}

v,err := xml.MarshalIndent(p," ")
if err != nil {
    fmt.Println("marshal xml value error,err.Error())
}

fmt.Println("convert map to xml value",string(v))

上面是使用xml过程中,碰到的问题.有不正确的地方,欢迎大家留言讨论,

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读