Go語言encode編碼解碼
在當前互聯網時代中,最常用的獨立於語言的數據格式有xml, Yaml,json,protobuf,Go 同樣的也支持這些數據格式的相關的操作,以下為對比表格。
| 名稱 | XML | YAML | JSON | Protocol Buffers |
|---|---|---|---|---|
| 數據結構 | 復雜 | 較簡單 | 簡單 | 較復雜 |
| 保存方式 | 文本 | 文本 | 文本 | 二進制 |
| 保存大小 | 大 | 中 | 中 | 小 |
| 解析效率 | 慢 | 中 | 中 | 快 |
| 語言支持 | 非常多 | 多 | 很多 | 較多 |
| 開發難度 | 繁瑣 | 較簡單 | 簡單 | 簡單 |
| 學習成本 | 低 | 低 | 低 | 低 |
| 適用范圍 | 數據交換 | 配置文件 | 數據交換 | 數據交換 |
TIP
在 go 中,如果想要對結構體進行序列化與反序列化,字段必須是對外暴露的,即首字母大寫。
另外,TOML 也逐漸開始流行,語法上像是.ini的改進,感興趣可以前往TOML:Tom 的(語義)明顯、(配置)最小化的語言了解一下。
XML
xml又名 eXtensible Markup Language,是用於存儲數據的一種格式,起源於 20 世紀 60 年代,是以上幾種數據格式中最為古老的一種。它的用途十分廣泛,可用於網絡傳輸,數據交換,配置文件,數據存儲等等。但隨著時代的更替,逐漸正在被新的標記語言替代。
首先定義結構體
type Person struct {
UserId string `xml:"id"`
Username string `xml:"name"`
Age int `xml:"age"`
Address string `xml:"address"`
}func Marshal(v any) ([]byte, error) //xml序列化
func MarshalIndent(v any, prefix, indent string) ([]byte, error) //格式化
func Unmarshal(data []byte, v any) error //反序列化序列化
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))
}輸出
<Person>
<id>120</id>
<name>jack</name>
<age>18</age>
<address>usa</address>
</Person>反序列化
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
}
}輸出
{UserId:120 Username:jack Age:18 Address:usa}不過傳統的 xml 解析方式經常需要新建結構體,這會十分的繁瑣,現在解析的都是簡單的 xml 結構,倘若使用復雜的結構,就會讓人十分頭疼。所以我們大多數會用一個第三方開源庫etree來解析 xml,感興趣的可以自行了解:Go 比較好用的解析 xml 文件的插件 etree - 掘金 (juejin.cn)。
YML
YAML 的語法和其他高級語言類似,並且可以簡單表達清單、散列表,標量等數據形態。它使用空白符號縮進和大量依賴外觀的特色,特別適合用來表達或編輯數據結構、各種配置文件,YML 也在許多項目裡以配置文件的形式存在,它的內容結構更加簡潔,一目了然。go 官方並沒有提供對於 YML 的支持,我們需要使用第三方包。
go get github.com/go-yaml/yaml主要方法
func Marshal(in interface{}) (out []byte, err error) //序列化
func Unmarshal(in []byte, out interface{}) (err error) //反序列化先准備結構體
type Config struct {
Database string `yaml:"database"`
Url string `yaml:"url"`
Port int `yaml:"port"`
Username string `yaml:"username"`
Password string `yaml:"password"`
}配置文件
database: mysql
url: 127.0.0.1
port: 3306
username: root
password: 123456序列化
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))
}輸出
database: oracle
url: localhost
port: 3326
username: root
password: "123456"不過由於yml本身有著嚴格的縮進語法,所以也不存在什麼序列化格式化的問題了。
反序列化
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
json在Restful風格的接口通信中經常會用到,其相較於xml更輕便的大小,低廉的學習成本使其在web領域稱為了主流的數據交換格式。
在 go 中,encoding/json包下提供對應的函數來進行 json 的序列化與反序列化,主要使用的有如下函數。
func Marshal(v any) ([]byte, error) //將go對象序列化為json字符串
func Unmarshal(data []byte, v any) error //將json字符串反序列化為go對象首先定義結構體
type Person struct {
UserId string
Username string
Age int
Address string
}序列化
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))
}結果
{ "UserId": "120", "Username": "jack", "Age": 18, "Address": "usa" }字段重命名
我們可以通過結構體標簽來達到重命名的效果。
type Person struct {
UserId string `json:"id"`
Username string `json:"name"`
Age int `json:"age"`
Address string `json:"address"`
}此時輸出
{ "id": "1202", "name": "jack", "age": 19, "address": "USA" }縮進
序列化時默認是沒有任何縮進的,這是為了減少傳輸過程的空間損耗,但是這並不利於人為觀察,在一些情況下我們需要將其序列化成人類能夠觀察的形式。為此,只需要換一個函數。
func MarshalIndent(v any, prefix, indent string) ([]byte, error)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))
}輸出如下
{
"id": "1202",
"name": "jack",
"age": 19,
"address": "USA"
}反序列化
在反序列化時需要注意,如果結構體有 json 標簽的話,則字段名優先以 json 標簽為准,否則以結構體屬性名為准。
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)
}輸出
{UserId:120 Username:jack Age:18 Address:usa}Protocol Buffers
protocol 是谷歌 2008 開源的語言中立,協議中立,可擴展的結構化數據序列化機制。相比於以上三種更加的輕便,而且在解包封包的時候更加的快速,多用於 RPC 領域通信相關,有關Protobuf的講解可以前往Protobuf。
安裝依賴
go get github.com/golang/protobuf/protoperson.proto文件
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;
}生成文件後
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文件生成對應語言的源代碼。
