转自: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