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

本文为转载,原文: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操作

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,772评论 6 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,458评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,610评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,640评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,657评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,590评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,962评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,631评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,870评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,611评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,704评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,386评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,969评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,944评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,179评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,742评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,440评论 2 342