Skip to content

Go語言encode編碼解碼

在當前互聯網時代中,最常用的獨立於語言的數據格式有xmlYamljsonprotobuf,Go 同樣的也支持這些數據格式的相關的操作,以下為對比表格。

名稱XMLYAMLJSONProtocol Buffers
數據結構復雜較簡單簡單較復雜
保存方式文本文本文本二進制
保存大小
解析效率
語言支持非常多很多較多
開發難度繁瑣較簡單簡單簡單
學習成本
適用范圍數據交換配置文件數據交換數據交換

TIP

在 go 中,如果想要對結構體進行序列化與反序列化,字段必須是對外暴露的,即首字母大寫。

另外,TOML 也逐漸開始流行,語法上像是.ini的改進,感興趣可以前往TOML:Tom 的(語義)明顯、(配置)最小化的語言了解一下。

XML

xml又名 eXtensible Markup Language,是用於存儲數據的一種格式,起源於 20 世紀 60 年代,是以上幾種數據格式中最為古老的一種。它的用途十分廣泛,可用於網絡傳輸,數據交換,配置文件,數據存儲等等。但隨著時代的更替,逐漸正在被新的標記語言替代。

首先定義結構體

go
type Person struct {
   UserId   string `xml:"id"`
   Username string `xml:"name"`
   Age      int    `xml:"age"`
   Address  string `xml:"address"`
}
go
func Marshal(v any) ([]byte, error) //xml序列化

func MarshalIndent(v any, prefix, indent string) ([]byte, error) //格式化

func Unmarshal(data []byte, v any) error //反序列化

序列化

go
func main() {
   person := Person{
      UserId:   "120",
      Username: "jack",
      Age:      18,
      Address:  "usa",
   }

   bytes, err := xml.MarshalIndent(person, "", "\t")
   if err != nil {
      fmt.Println(err)
      return
   }
   fmt.Println(string(bytes))
}

輸出

xml
<Person>
        <id>120</id>
        <name>jack</name>
        <age>18</age>
        <address>usa</address>
</Person>

反序列化

go
func main() {
   var person = Person{
      UserId:   "",
      Username: "",
      Age:      0,
      Address:  "",
   }

   xmlStr := "<Person>                      \n        <id>120</id>          \n        <name>jack</name>     \n        <age>18</age>         \n        <address>usa</address>\n</Person>  "

   err := xml.Unmarshal([]byte(xmlStr), &person)
   if err != nil {
      fmt.Println(err)
      return
   }
}

輸出

go
{UserId:120 Username:jack Age:18 Address:usa}

不過傳統的 xml 解析方式經常需要新建結構體,這會十分的繁瑣,現在解析的都是簡單的 xml 結構,倘若使用復雜的結構,就會讓人十分頭疼。所以我們大多數會用一個第三方開源庫etree來解析 xml,感興趣的可以自行了解:Go 比較好用的解析 xml 文件的插件 etree - 掘金 (juejin.cn)

YML

YAML 的語法和其他高級語言類似,並且可以簡單表達清單、散列表,標量等數據形態。它使用空白符號縮進和大量依賴外觀的特色,特別適合用來表達或編輯數據結構、各種配置文件,YML 也在許多項目裡以配置文件的形式存在,它的內容結構更加簡潔,一目了然。go 官方並沒有提供對於 YML 的支持,我們需要使用第三方包。

powershell
go get github.com/go-yaml/yaml

主要方法

go
func Marshal(in interface{}) (out []byte, err error) //序列化

func Unmarshal(in []byte, out interface{}) (err error) //反序列化

先准備結構體

go
type Config struct {
   Database string `yaml:"database"`
   Url      string `yaml:"url"`
   Port     int    `yaml:"port"`
   Username string `yaml:"username"`
   Password string `yaml:"password"`
}

配置文件

yaml
database: mysql
url: 127.0.0.1
port: 3306
username: root
password: 123456

序列化

go
func main() {
   config := Config{
      Database: "oracle",
      Url:      "localhost",
      Port:     3326,
      Username: "root",
      Password: "123456",
   }

   out, err := yaml.Marshal(config)
   if err != nil {
      fmt.Println(err)
      return
   }
   fmt.Println(string(out))
}

輸出

yaml
database: oracle
url: localhost
port: 3326
username: root
password: "123456"

不過由於yml本身有著嚴格的縮進語法,所以也不存在什麼序列化格式化的問題了。

反序列化

go
func main() {
   bytes, err := os.ReadFile("./src/config.yml")
   if err != nil {
      fmt.Println(err)
      return
   }
   var config Config
   err = yaml.Unmarshal(bytes, &config)
   if err != nil {
      fmt.Println(err)
      return
   }

   fmt.Println(config)
}

輸出

{mysql 127.0.0.1 3306 root 123456}

JSON

jsonRestful風格的接口通信中經常會用到,其相較於xml更輕便的大小,低廉的學習成本使其在web領域稱為了主流的數據交換格式。

在 go 中,encoding/json包下提供對應的函數來進行 json 的序列化與反序列化,主要使用的有如下函數。

go
func Marshal(v any) ([]byte, error) //將go對象序列化為json字符串

func Unmarshal(data []byte, v any) error //將json字符串反序列化為go對象

首先定義結構體

go
type Person struct {
   UserId   string
   Username string
   Age      int
   Address  string
}

序列化

go
func main() {
  person := Person{
    UserId:   "120",
    Username: "jack",
    Age:      18,
    Address:  "usa",
  }

  bytes, err := json.Marshal(person)
  if err != nil {
    fmt.Println(err)
    return
  }
  fmt.Println(string(bytes))
}

結果

json
{ "UserId": "120", "Username": "jack", "Age": 18, "Address": "usa" }

字段重命名

我們可以通過結構體標簽來達到重命名的效果。

go
type Person struct {
   UserId   string `json:"id"`
   Username string `json:"name"`
   Age      int    `json:"age"`
   Address  string `json:"address"`
}

此時輸出

json
{ "id": "1202", "name": "jack", "age": 19, "address": "USA" }

縮進

序列化時默認是沒有任何縮進的,這是為了減少傳輸過程的空間損耗,但是這並不利於人為觀察,在一些情況下我們需要將其序列化成人類能夠觀察的形式。為此,只需要換一個函數。

go
func MarshalIndent(v any, prefix, indent string) ([]byte, error)
go
func main() {
   person := Person{
      UserId:   "1202",
      Username: "jack",
      Age:      19,
      Address:  "USA",
   }
   bytes, err := json.MarshalIndent(person, "", "\t")
   if err != nil {
      fmt.Println(err)
      return
   }
   fmt.Println(string(bytes))
}

輸出如下

json
{
  "id": "1202",
  "name": "jack",
  "age": 19,
  "address": "USA"
}

反序列化

在反序列化時需要注意,如果結構體有 json 標簽的話,則字段名優先以 json 標簽為准,否則以結構體屬性名為准。

go
func main() {
  person := Person{}

  jsonStr := "{\"id\":\"120\",\"name\":\"jack\",\"age\":18,\"address\":\"usa\"}\n"

  err := json.Unmarshal([]byte(jsonStr), &person)
  if err != nil {
    fmt.Println(err)
    return
  }
  fmt.Printf("%+v", person)
}

輸出

go
{UserId:120 Username:jack Age:18 Address:usa}

Protocol Buffers

protocol 是谷歌 2008 開源的語言中立,協議中立,可擴展的結構化數據序列化機制。相比於以上三種更加的輕便,而且在解包封包的時候更加的快速,多用於 RPC 領域通信相關,有關Protobuf的講解可以前往Protobuf

安裝依賴

go get github.com/golang/protobuf/proto

person.proto文件

protobuf
syntax = "proto3";

option go_package = "./;person";

package proto;

enum Gender{
  MAIL = 0;
  FE_MAIL = 1;
}

message person {
  string name = 1;
  int32 age = 2;
  Gender gender = 3;
}

生成文件後

go
package main

import (
   p "GoProject/src/proto"
   "fmt"
   "github.com/golang/protobuf/proto"
)

func main() {
  person := p.Person{
    Name:   "wyh",
    Age:    12,
    Gender: p.Gender_FE_MAIL,
  }

  data, err := proto.Marshal(&person)//序列化
  if err != nil {
    fmt.Println(err)
    return
  }
  temp := &p.Person{}
  fmt.Println("proto buffer len: ", len(data), "bytes:", data)
  err = proto.Unmarshal(data, temp)//反序列化
  if err != nil {
    fmt.Println(err)
    return
  }
  fmt.Println(temp)
}

輸出

proto buffer len:  9 bytes: [10 3 119 121 104 16 12 24 1]
name:"wyh"  age:12  gender:FE_MAIL

不過通常我們不會去手動序列化,protoc編譯器可以根據我們定義好的proto文件生成對應語言的源代碼。

Golang學習網由www.golangdev.cn整理維護