转自:https://www.jianshu.com/p/772ca3c6c7ed
包名:encoding/json 在程序开发过程中最常见的就是讲字符串以及json之间的转化。在Golang中需要先定义一个json字符串的结构体来作为转换介质。
marshal和unmarshal
常用的几个方法函数:
//将接口v中的数据解析成json格式的[]byte func Marshal(v interface{}) ([]byte, error) //按照一定格式和缩进方式格式化json字符串 func MarshalIndent(v interface{}, prefix, indent string) ([]byte, error) //将json字符串转换成[]byte,不能够读入接口v中 func Unmarshal(data []byte, v interface{}) error需要注意的问题:
/* 针对json的输出,我们定义struct tag的时候需要注意: (1)字段的tag是"-",该字段不会输出到JSON (2)tag中带有自定义名称,该自定义名称会出现在json的字段名中 (3)tag中带有"omitempty"选项,该字段值为空,不会输出到json (4)如果字段类型为bool,string,int,int64等,而tag中带有",string"选项,该字段输出到json的时候会把该字段对应的值转换成json字符串 Marshal函数只有在转换成功的时候才会返回数据,所以转换过程需要注意以下几点: (1)JSON对象只支持string作为key,所编码一个map,那么必须是map[string]T这种类型 (2)Channel,complex和function是不能被编码成JSON的 (3)嵌套的数据是不能编码的,不然会让json编码进入死循环 (4)指针在编码的二十号会输出指针指向的内容,而指针会输出null */示例:
# 将golang内部对象格式化成json串 $ cat marshal-test.go package main import ( "fmt" "encoding/json" _ "bytes" "os" ) type Serverslice struct{ Servers []Server `json:"servers"` } type Server struct{ ServerName string `json:"servername"` ServerIP string `json:"serverip,omitempty"` } func main() { var s Serverslice //func append(slice []Type, elems ...Type) []Type s.Servers = append(s.Servers,Server{ServerName:"Beijing",ServerIP:"10.0.0.1"}) s.Servers = append(s.Servers,Server{ServerName:"Xi'an",ServerIP:"10.0.0.2"}) //slice里面嵌套结构体[{},{}] 遍历出来的是slice里面包含json串 ss := []Server{{"Beijing","172.0.0.1"},{"Shanghai","172.0.0.2"}} b,err := json.Marshal(s) if err != nil { os.Exit(1) } fmt.Println(string(b)) bb,err := json.MarshalIndent(ss,""," ") if err == nil { fmt.Println(string(bb)) } } //第一个是结构体内部嵌套[]struct //第二个是[]struct来初始化结构体对象的 $ go run marshal-test.go {"servers":[{"servername":"Beijing","serverip":"10.0.0.1"},{"servername":"Xi'an","serverip":"10.0.0.2"}]} [ { "servername": "Beijing", "serverip": "172.0.0.1" }, { "servername": "Shanghai", "serverip": "172.0.0.2" } ] # 将json字符串读取后转化成内部的对象实例 $ cat unmarshal.go package main import ( "fmt" "encoding/json" ) type Server struct { ServerName string `json:"servername"` ServerIP string `json:"serverip"` } //数组对应slice type Serverslice struct{ Servers []Server } func main() { //初始化一个json字符串 str := `{"servers":[{"servername":"Beijing","serverip":"10.0.0.1"},{"servername":"Xi'an","serverip":"10.0.0.2"}]}` //func Unmarshal(data []byte, v interface{}) error //初始化一个接口对象,用来存储json对象元素 var s Serverslice //将json的字符串转换成s对象(这里用的是指针的方式,所以可以直接修改底层结构体中的数据) //需要读取的json字符串必须先写入[]byte类型的对象中(二进制对象文件) if err := json.Unmarshal([]byte(str),&s); err == nil { fmt.Println(s) fmt.Println(s.Servers) fmt.Println(s.Servers[0].ServerName,s.Servers[1].ServerName) } }decode和encode
相关函数和方法:
//从一个输入流中读取并进行解码json的值 type Decoder struct { // contains filtered or unexported fields } //初始化一个Decoder对象 func NewDecoder(r io.Reader) *Decoder //Decoder对象拥有的方法 func (dec *Decoder) Buffered() io.Reader func (dec *Decoder) Decode(v interface{}) error func (dec *Decoder) More() bool func (dec *Decoder) Token() (Token, error) func (dec *Decoder) UseNumber() //将json字符串的值编码到输出流中 type Encoder struct { // contains filtered or unexported fields } //初始化一个Encoder对象 func NewEncoder(w io.Writer) *Encoder //将json字符串中的内容编码到接口v的输出流中,其实和Marshal底层差不多 func (enc *Encoder) Encode(v interface{}) error func (enc *Encoder) SetEscapeHTML(on bool) func (enc *Encoder) SetIndent(prefix, indent string)decode示例:
$ cat decoder.go package main import ( "encoding/json" "fmt" "io" "log" "strings" ) func main() { const jsonStream = ` {"Name": "Ed", "Text": "Knock knock."} {"Name": "Sam", "Text": "Who's there?"} {"Name": "Ed", "Text": "Go fmt."} {"Name": "Sam", "Text": "Go fmt who?"} {"Name": "Ed", "Text": "Go fmt yourself!"} ` type Message struct { Name, Text string } //初始化一个Decoder对象 //func NewDecoder(r io.Reader) *Decoder dec := json.NewDecoder(strings.NewReader(jsonStream)) for { var m Message //创建的decoder对象的Decide方法可以将内容解析到接口v中 //func (dec *Decoder) Decode(v interface{}) error //读取到末尾和读取错误 if err := dec.Decode(&m); err == io.EOF { break } else if err != nil { log.Fatal(err) } fmt.Printf("%s: %s\n", m.Name, m.Text) } } $ go run decode.go Ed: Knock knock. Sam: Who's there? Ed: Go fmt. Sam: Go fmt who? Ed: Go fmt yourself!hex包主要用来编码和解码16进制的字符串 主要函数:
//将src解码到[]byte类型的dst(长度为DecodedLen(len(src)))中,并且返回dst的长度 func Decode(dst, src []byte) (int, error) //解码16进制的字符串为一个[]byte func DecodeString(s string) ([]byte, error) //x个byte解码后的长度,一般是x/2 func DecodedLen(x int) int //返回data的dump字符串,格式类似于`hexdump -C`命令行输出 func Dump(data []byte) string func Dumper(w io.Writer) io.WriteCloser //将src编码到dst中(固定的长度为EncodedLen(len(src)) ) func Encode(dst, src []byte) int //将[]byte转换成字符串16进制的字符串 func EncodeToString(src []byte) string //返回一个编码长度,一般为n的2倍 func EncodedLen(n int) int示例:
$ cat hex.go package main import ( "encoding/hex" "fmt" ) func Something() { //func EncodeToString(src []byte) string 编码byte字节为16进制字符串 src := []byte("hello") fmt.Println(src) //[104 101 108 108 111] encodeStr := hex.EncodeToString(src) //68656c6c6f 16进制转换 fmt.Println(encodeStr) //func Encode(dst, src []byte) int //func EncodedLen(n int) int Welcome := []byte("Gopher!") Wdest := make([]byte, hex.EncodedLen(len(Welcome))) num := hex.Encode(Wdest, Welcome) fmt.Println(Wdest, num) //num=14 //func DecodeString(s string) ([]byte, error) 解码16进制的字符串为byte类型 decodeStr, _ := hex.DecodeString(encodeStr) fmt.Println(string(decodeStr)) //func DecodedLen(x int) int x个byte解码后的长度,一般是x/2 //func Decode(dst, src []byte) (int, error) 将byte类型的src解码到byte类型的dst中,并且返回dst的长度 test := []byte("48656c6c6f20476f7068657221") dest := make([]byte, hex.DecodedLen(len(test))) //定义一个切片 num, err := hex.Decode(dest, test) //转换16进制字符串为byte[]类型,返回切片长度 if err != nil { return } fmt.Println(num, dest[:num], string(dest), len(dest), cap(dest)) // print 13 //func Dump(data []byte) string //返回给定字符串以及字符串相对应的hex dump文件 效果相当于linux命令行下的"hexdump -C filename" content := []byte("Go is an open source programming language.") fmt.Println(hex.Dump(content)) } func main() { Something() } $ go run hex.go [104 101 108 108 111] 68656c6c6f [52 55 54 102 55 48 54 56 54 53 55 50 50 49] 14 hello 13 [72 101 108 108 111 32 71 111 112 104 101 114 33] Hello Gopher! 13 13 00000000 47 6f 20 69 73 20 61 6e 20 6f 70 65 6e 20 73 6f |Go is an open so| 00000010 75 72 63 65 20 70 72 6f 67 72 61 6d 6d 69 6e 67 |urce programming| 00000020 20 6c 61 6e 67 75 61 67 65 2e | language.|gob包主要用来管理在二进制的字节流之间进行编码和解码的事物上。一个典型的使用案例就是使用net/rpc包传输在远程过程调用(RPC)中的参数和结果。
import "encoding/gob"1.一个模拟网络中的字节流转换
示例说明:定义两个结构体P,Q,前者用于gob在一个网络中编码(encoder)的数据结构,后者用于解码(decoder)的数据结构。
package main /* 使用`encoding/gob`包的一个简单用法,创建一个编码器(encoder),传输一些值,然后使用解码器(decoder)进行接收 */ import ( "bytes" "encoding/gob" "fmt" "log" ) type P struct { X, Y, Z int Name string } type Q struct { X, Y *int32 Name string } func main() { // 初始化一个encoder和decoder.t通常encoder和decoder将通过网络连接,并且两者在不同的进程中运行 var network bytes.Buffer // 使用buffer模拟一个网络连接(二进制字节流) enc := gob.NewEncoder(&network) // 编码一些数据到网络中 dec := gob.NewDecoder(&network) // 从网络中读取编码并解析 /* //NewDecoder初始化一个decoder对象,返回空的Decoder结构体 func NewDecoder(r io.Reader) *Decoder // Decoder结构体方法 func (dec *Decoder) Decode(e interface{}) error func (*Decoder) DecodeValue //NewEncoder初始化一个encoder对象,并返回Encoder机构体 func NewEncoder(w io.Writer) *Encoder // Encoder结构体方法 func (enc *Encoder) Encode(e interface{}) error func (enc *Encoder) EncodeValue(value reflect.Value) error */ // 使用enc进行发送一些编码的数据 // 使用enc.Encode方法进行编码两组数据 err := enc.Encode(P{6, 6, 8, "xxbandy.github.io"}) if err != nil { log.Fatal("encode error:", err) } err = enc.Encode(P{1024, 2048, 1000, "BG彪"}) if err != nil { log.Fatal("encode error:", err) } // 使用dec进行解码二进制数据并打印这些值 // 初始化结构体变量,并将网络连接(&network)中的数据按照结构体Q的实例q进行解码 var q Q err = dec.Decode(&q) if err != nil { log.Fatal("decode error 1:", err) } fmt.Printf("%q: {%d, %d}\n", q.Name, *q.X, *q.Y) err = dec.Decode(&q) if err != nil { log.Fatal("decode error 2:", err) } fmt.Printf("%q: {%d, %d}\n", q.Name, *q.X, *q.Y) }运行效果:
$ go run simple-encoder-decoder.go "xxbandy.github.io": {6, 6} "BG彪": {1024, 2048}2.通过自定义的Encode和Decode方法工具来传输值
通过接口的方式转换结构体变量中的私有变量
package main import ( "bytes" "encoding/gob" "fmt" "log" ) // Vector 结构体有一些导出的域,这些包不能够被访问,因此我们需要使用`gob`包 // 写一个BinaryMarshal/BinaryUnmarshal方法对来允许我们去发送和接收该类型的数据. // 这些接口都被定义在了`encoding`包中 // 其实等同于当前包中定义的`GobEncode/GobDecoder`接口 type Vector struct { x, y, z int } // Vector的MarshalBinary方法 func (v Vector) MarshalBinary() ([]byte, error) { // A simple encoding: plain text. // 一个简单的纯文本编码示例 var b bytes.Buffer fmt.Fprintln(&b, v.x, v.y, v.z) return b.Bytes(), nil } // Vector的UnmarshalBinary方法修改了接受者方法,必须接收一个指针 func (v *Vector) UnmarshalBinary(data []byte) error { b := bytes.NewBuffer(data) _, err := fmt.Fscanln(b, &v.x, &v.y, &v.z) return err } // 使用自定义的encoding和decoding方法来传输数据 func main() { // 使用buffer伪造一个网络连接 var network bytes.Buffer // 初始化一个编码器encoder并发送一段数据 enc := gob.NewEncoder(&network) //因为Vector中的元素都是私有变量不能被外部调用,需要默认定义相关的方法 // 疑问:为啥Vector结构体相关的方法会自动执行内部的MarshalBinary和UnmarshalBinary方法 err := enc.Encode(Vector{3, 4, 5}) if err != nil { log.Fatal("encode:", err) } // 创建一个解码器(decoder)并接收一个值 dec := gob.NewDecoder(&network) var v Vector err = dec.Decode(&v) if err != nil { log.Fatal("decode:", err) } fmt.Println(v) }输出示例:
$ go run gob-EncoderDecoder.go {3 4 5}3.使用gob包来传输接口类型的数据
使用gob包来编码、解码并传输接口类型数据需要使用gob.Register(value interface{})对指定类型数据进行注册。
/** * @File Name: gob-interface.go * @Author: xxbandy @http://xxbandy.github.io * @Email: * @Create Date: 2018-03-12 14:03:10 * @Last Modified: 2018-03-12 14:03:23 * @Description: */ package main import ( "bytes" "encoding/gob" "fmt" "log" "math" ) type Point struct { X, Y int } // 定义一个斜边长度 func (p Point) Hypotenuse() float64 { return math.Hypot(float64(p.X), float64(p.Y)) } //定义毕达哥拉斯接口,其中有很多著名的定理,勾股定理就是其一 type Pythagoras interface { Hypotenuse() float64 } // 该示例演示如何去编码一个接口类型的值 // 和正则类型的区别是注册一个具体的类型而不是实现该接口 func main() { // 构造一个网络连接 var network bytes.Buffer // 首先必须为encoder何decoder注册一个具体的类型 // 随后该具体的类型将发送一个生命去实现该接口 // func Register(value interface{}) gob.Register(Point{}) // 创建一个encoder并发送数据 enc := gob.NewEncoder(&network) for i := 1; i <= 3; i++ { interfaceEncode(enc, Point{3 * i, 4 * i}) } // 创建一个decoder解码数据并返回 dec := gob.NewDecoder(&network) for i := 1; i <= 3; i++ { result := interfaceDecode(dec) fmt.Println(result.Hypotenuse()) } } // interfaceEncode 函数编码一个接口类型的值到encoder示例中 func interfaceEncode(enc *gob.Encoder, p Pythagoras) { // 需要再调用的时候优先进行注册指定的类型,否则会失败 // 需要传送一个指针给接口去编码一个接口类型,如果我们直接去传一个p,将会变成具体类型去代替 err := enc.Encode(&p) if err != nil { log.Fatal("encode:", err) } } // interfaceDecode 从字节流中解码下一个接口类型的值并返回它 // 返回一个Pythagoras 接口类型的值 func interfaceDecode(dec *gob.Decoder) Pythagoras { // decode将失败除非在链接中具体的类型已被注册(Point{}需要先使用gob.Register()注册才可以进行解码) // 一般情况下我们会在主函数调用中去注册 var p Pythagoras err := dec.Decode(&p) if err != nil { log.Fatal("decode:", err) } return p } $ go run gob-interface.go 5 10 15