美文网首页Golang程序员go语言学习
Golang 学习笔记(09)—— json和xml解析

Golang 学习笔记(09)—— json和xml解析

作者: ChainZhang | 来源:发表于2018-01-03 13:58 被阅读96次

    本文为转载,原文:Golang 学习笔记(09)—— json和xml解析

    Golang

    xml

    package

    import "encoding/xml"
    

    函数

    Marshal

    func Marshal(v interface{}) ([]byte, error)
    

    Marshal函数返回v的XML编码。

    Marshal处理数组或者切片时会序列化每一个元素。Marshal处理指针时,会序列化其指向的值;如果指针为nil,则啥也不输出。Marshal处理接口时,会序列化其内包含的具体类型值,如果接口值为nil,也是不输出。Marshal处理其余类型数据时,会输出一或多个包含数据的XML元

    Unmarshal

    func Unmarshal(data []byte, v interface{}) error
    

    Unmarshal解析XML编码的数据并将结果存入v指向的值。v只能指向结构体、切片或者和字符串。良好格式化的数据如果不能存入v,会被丢弃。

    因为Unmarshal使用reflect包,它只能填写导出字段。本函数好似用大小写敏感的比较来匹配XML元素名和结构体的字段名/标签键名。

    例子

    package main
    
    import (
        "fmt"
        "encoding/xml"
    )
    
    type Address struct{
        City string
        Area string
    }
    
    type Email struct{
        Where string `xml:"where,attr"`
        Addr string
    }
    
    type Student struct{
        Id int `xml:"id,attr"`
        Address
        Email []Email
        FirstName string `xml:"name>first"`
        LastName string `xml:"name>last"`
    }
    
    func main(){
        //实例化对象
        stu := Student{23, Address{"shanghai","pudong"},[]Email{Email{"home","home@qq.com"}, Email{"work","work@qq.com"}},"chain","zhang"}
        fmt.Println("stu:", stu)
        //序列化
        buf,err := xml.Marshal(stu)
        if err != nil{
            fmt.Println(err.Error())
            return
        }
        fmt.Println("xml: ", string(buf))
    
        var newStu Student
        //反序列化
        err1 := xml.Unmarshal(buf, &newStu)
        if err1 != nil{
            fmt.Println(err1.Error())
            return
        }
        fmt.Println("newStu: ", newStu)
    }
    
    运行结果

    NewEncoder

    func NewEncoder(w io.Writer) *Encoder
    

    NewEncoder创建一个写入w的*Encoder。

    (*Encoder) Encode

    func (enc *Encoder) Encode(v interface{}) error
    

    Encode将v编码为XML后写入底层。

    NewDecoder

    func NewDecoder(r io.Reader) *Decoder
    

    创建一个从r读取XML数据的解析器。如果r未实现io.ByteReader接口,NewDecoder会为其添加缓存。

    (*Decoder) Decode

    func (d *Decoder) Decode(v interface{}) error
    

    Decode方法功能类似xml.Unmarshal函数,但会从底层读取XML数据并查找StartElement。

    示例

    package main
    
    import (
        "os"
        "fmt"
        "encoding/xml"
    )
    
    type Address struct{
        City string
        Area string
    }
    
    type Email struct{
        Where string `xml:"where,attr"`
        Addr string
    }
    
    type Student struct{
        Id int `xml:"id,attr"`
        Address
        Email []Email
        FirstName string `xml:"name>first"`
        LastName string `xml:"name>last"`
    }
    
    func main(){
        t2()
    }
    
    func t2(){
        f, err := os.Create("d:/myxml.xml")
        if err != nil{
            fmt.Println("err: ", err.Error())
            return
        }
        defer f.Close()
        //实例化对象
        stu := Student{23, Address{"shanghai","pudong"},[]Email{Email{"home","home@qq.com"}, Email{"work","work@qq.com"}},"chain","zhang"}
        fmt.Println("stu: ", stu)
        //序列化到文件中
        encoder := xml.NewEncoder(f)
        err1 := encoder.Encode(stu)
        if err1 != nil{
            fmt.Println("err1: ", err1.Error())
            return
        }
        //重置文件指针
        f.Seek(0, os.SEEK_SET)
        var newStu Student
        //反序列化到newStu对象
        decoder := xml.NewDecoder(f)
        err2 := decoder.Decode(&newStu)
        if err2 != nil{
            fmt.Println("err2: ", err2.Error())
            return
        }
        fmt.Println("new stu: ", newStu)
    }
    
    运行结果
    文件内容

    Token

    处了上面的方法,xml包还提供了其他的解析xml方法。在.net,java中都提供了XMLReader类来解析xml,在go中也有类似的方法。
    (*Decoder) Token

    func (d *Decoder) Token() (t Token, err error)Token
    

    返回输入流里的下一个XML token。在输入流的结尾处,会返回(nil, io.EOF)

    返回的token数据里的[]byte数据引用自解析器内部的缓存,只在下一次调用Token之前有效。如要获取切片的拷贝,调用CopyToken函数或者token的Copy方法。

    成功调用的Token方法会将自我闭合的元素(如
    )扩展为分离的起始和结束标签。

    Token方法会保证它返回的StartElement和EndElement两种token正确的嵌套和匹配:如果本方法遇到了不正确的结束标签,会返回一个错误。

    我们看下面的例子

    package main
    
    import (
        "os"
        "fmt"
        "encoding/xml"
    )
    
    type Address struct{
        City string
        Area string
    }
    
    type Email struct{
        Where string `xml:"where,attr"`
        Addr string
    }
    
    type Student struct{
        Id int `xml:"id,attr"`
        Address
        Email []Email
        FirstName string `xml:"name>first"`
        LastName string `xml:"name>last"`
    }
    
    func main(){
        XmlT3()
    }
    
    func XmlT3(){
        f, err := os.Create("d:/myxml.xml")
        if err != nil{
            fmt.Println("err: ", err.Error())
            return
        }
        defer f.Close()
        //实例化对象
        stu := Student{23, Address{"shanghai","pudong"},[]Email{Email{"home","home@qq.com"}, Email{"work","work@qq.com"}},"chain","zhang"}
        fmt.Println("stu: ", stu)
        //序列化到文件中
        encoder := xml.NewEncoder(f)
        err1 := encoder.Encode(stu)
        if err1 != nil{
            fmt.Println("err1: ", err1.Error())
            return
        }
        //重置文件指针
        f.Seek(0, os.SEEK_SET)
        decoder := xml.NewDecoder(f)    
        var strName string
        for{
            token, err2 := decoder.Token()
            if err2 != nil{
                break
            }
            switch t := token.(type){
            case xml.StartElement:
                stelm := xml.StartElement(t)
                fmt.Println("start: ", stelm.Name.Local)
                strName = stelm.Name.Local
            case xml.EndElement:
                endelm := xml.EndElement(t)
                fmt.Println("end: ", endelm.Name.Local)
            case xml.CharData:
                data := xml.CharData(t)
                str := string(data)
                switch strName{
                case "City":
                    fmt.Println("city:", str)
                case "first":
                    fmt.Println("first: ", str)
                }
            }
        }
    }
    
    运行结果

    上面这几种方法,Token解析是最快的。对于大文件解析,对于性能要求高时,这种方式是最好的选择

    json

    package

    import "encoding/json"
    

    函数

    Marshal

    func Marshal(v interface{}) ([]byte, error)
    

    Marshal函数返回v的json编码。

    Marshal函数会递归的处理值。如果一个值实现了Marshaler接口切非nil指针,会调用其MarshalJSON方法来生成json编码。nil指针异常并不是严格必需的,但会模拟与UnmarshalJSON的行为类似的必需的异常。

    否则,Marshal函数使用下面的基于类型的默认编码格式:

    布尔类型编码为json布尔类型。

    浮点数、整数和Number类型的值编码为json数字类型。

    字符串编码为json字符串。角括号"<"和">"会转义为"\u003c"和"\u003e"以避免某些浏览器吧json输出错误理解为HTML。基于同样的原因,"&"转义为"\u0026"。

    数组和切片类型的值编码为json数组,但[]byte编码为base64编码字符串,nil切片编码为null。

    Unmarshal

    func Unmarshal(data []byte, v interface{}) error
    

    Unmarshal函数解析json编码的数据并将结果存入v指向的值。

    Unmarshal和Marshal做相反的操作,必要时申请映射、切片或指针,有如下的附加规则:

    要将json数据解码写入一个指针,Unmarshal函数首先处理json数据是json字面值null的情况。此时,函数将指针设为nil;否则,函数将json数据解码写入指针指向的值;如果指针本身是nil,函数会先申请一个值并使指针指向它。

    要将json数据解码写入一个结构体,函数会匹配输入对象的键和Marshal使用的键(结构体字段名或者它的标签指定的键名),优先选择精确的匹配,但也接受大小写不敏感的匹配。

    要将json数据解码写入一个接口类型值,函数会将数据解码为如下类型写入接口:

    Bool                   对应JSON布尔类型
    float64                对应JSON数字类型
    string                 对应JSON字符串类型
    []interface{}          对应JSON数组
    map[string]interface{} 对应JSON对象
    nil                    对应JSON的null
    

    如果一个JSON值不匹配给出的目标类型,或者如果一个json数字写入目标类型时溢出,Unmarshal函数会跳过该字段并尽量完成其余的解码操作。如果没有出现更加严重的错误,本函数会返回一个描述第一个此类错误的详细信息的UnmarshalTypeError。

    JSON的null值解码为go的接口、指针、切片时会将它们设为nil,因为null在json里一般表示“不存在”。 解码json的null值到其他go类型时,不会造成任何改变,也不会产生错误。

    当解码字符串时,不合法的utf-8或utf-16代理(字符)对不视为错误,而是将非法字符替换为unicode字符U+FFFD。

    例子

    package main
    
    import (
        "os"
        "fmt"
        "encoding/json"
    )
    
    type Address struct{
        City string `json:"ciry"`
        Area string `json:"area"`
    }
    
    type Email struct{
        Where string `json:"where"`
        Addr string `json:"addr"`
    }
    
    type Student struct{
        Id int `json:"id"`
        Address `json:"address"`
        Emails []Email `json:"emails"`
        FirstName string `json:"first_name"`
        LastName string `json:"last_name"`
    }
    
    func main(){
        JsonT1()
    }
    
    func JsonT1(){
        //实例化对象
        stu := Student{23, Address{"shanghai","pudong"},[]Email{Email{"home","home@qq.com"}, Email{"work","work@qq.com"}},"chain","zhang"}
        fmt.Println("stu: ", stu)
        //序列化对象到字符串
        buf, err1 := json.Marshal(stu)
        if err1 != nil{
            fmt.Println("err1: ", err1.Error())
            return
        }
        fmt.Println("json: ", string(buf))
        //反序列化字符串到对象
        var newStu Student
        err2 := json.Unmarshal(buf, &newStu)
        if err2 != nil{
            fmt.Println("err2: ", err2.Error())
            return
        }
        fmt.Println("new stu: ", newStu)
    }
    
    运行结果

    NewEncoder

    func NewEncoder(w io.Writer) *Encoder
    

    NewEncoder创建一个将数据写入w的*Encoder。

    (*Encoder) Encode

    func (enc *Encoder) Encode(v interface{}) error
    

    Encode将v的json编码写入输出流,并会写入一个换行符。

    NewDecoder

    func NewDecoder(r io.Reader) *Decoder
    

    NewDecoder创建一个从r读取并解码json对象的*Decoder,解码器有自己的缓冲,并可能超前读取部分json数据。

    (*Decoder) Decode

    func (dec *Decoder) Decode(v interface{}) error
    

    Decode从输入流读取下一个json编码值并保存在v指向的值里。

    例子

    package main
    
    import (
        "os"
        "fmt"
        "encoding/json"
    )
    
    type Address struct{
        City string `json:"ciry"`
        Area string `json:"area"`
    }
    
    type Email struct{
        Where string `json:"where"`
        Addr string `json:"addr"`
    }
    
    type Student struct{
        Id int `json:"id"`
        Address `json:"address"`
        Emails []Email `json:"emails"`
        FirstName string `json:"first_name"`
        LastName string `json:"last_name"`
    }
    
    func main(){
        JsonT2()
    }
    func JsonT2(){
        f, err1 := os.Create("d:/myjson.txt")
        if err1 != nil{
            fmt.Println("err1: ", err1.Error())
            return
        }
        defer f.Close()
        //实例化对象
        stu := Student{23, Address{"shanghai","pudong"},[]Email{Email{"home","home@qq.com"}, Email{"work","work@qq.com"}},"chain","zhang"}
        fmt.Println("stu: ", stu)
        //序列化
        encoder := json.NewEncoder(f)
        err2 := encoder.Encode(stu)
        if err2 != nil{
            fmt.Println("err2: ", err2.Error)
            return
        }
        //重置文件指针
        f.Seek(0, os.SEEK_SET)
        var newStu Student
        //反序列化
        decoder := json.NewDecoder(f)
        err3 := decoder.Decode(&newStu)
        if err3 != nil{
            fmt.Println("err3: ", err3.Error())
            return
        }
        fmt.Println("new stu: ", newStu)
    }
    
    运行结果
    文件保存内容

    转载请注明出处:
    Golang 学习笔记(09)—— json和xml解析

    目录
    上一节:Golang 学习笔记(08)—— 文件操作
    下一节:Golang 学习笔记(10)—— mysql操作

    相关文章

      网友评论

        本文标题:Golang 学习笔记(09)—— json和xml解析

        本文链接:https://www.haomeiwen.com/subject/beuhnxtx.html