我的专题

Golang下的encoding相关模块使用

2018-04-27  本文已影响310人  9c46ece5b7bd

encoding/json模块的使用

包名: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!

encoding/hex模块的使用

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.|

encoding/gob包的使用

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
上一篇下一篇

猜你喜欢

热点阅读