Codifica e Decodifica in Go
Nell'era Internet attuale, i formati di dati indipendenti dal linguaggio più comunemente utilizzati sono xml, Yaml, json, protobuf. Go supporta allo stesso modo operazioni relative a questi formati di dati. Di seguito è riportata una tabella comparativa.
| Nome | XML | YAML | JSON | Protocol Buffers |
|---|---|---|---|---|
| Struttura Dati | Complessa | Piuttosto Semplice | Semplice | Piuttosto Complessa |
| Modalità di Salvataggio | Testo | Testo | Testo | Binario |
| Dimensione Salvataggio | Grande | Media | Media | Piccola |
| Efficienza Parsing | Lenta | Media | Media | Veloce |
| Supporto Linguaggi | Moltissimi | Molti | Moltissimi | Abbastanza |
| Difficoltà Sviluppo | Laborioso | Piuttosto Semplice | Semplice | Semplice |
| Costo Apprendimento | Basso | Basso | Basso | Basso |
| Ambito di Applicazione | Scambio Dati | File di Configurazione | Scambio Dati | Scambio Dati |
TIP
In Go, se si desidera serializzare e deserializzare una struct, i campi devono essere esportati, ovvero la prima lettera deve essere maiuscola.
Inoltre, TOML sta iniziando a diventare popolare, la sintassi è simile a un miglioramento di .ini. Chi è interessato può dare un'occhiata a TOML: Tom's Obvious, Minimal Language.
XML
xml, acronimo di eXtensible Markup Language, è un formato utilizzato per archiviare dati, originato negli anni '60 del secolo scorso, ed è il più antico tra i formati di dati sopra menzionati. I suoi utilizzi sono molto ampi, tra cui trasmissione di rete, scambio di dati, file di configurazione, archiviazione dati e così via. Tuttavia, con il passare del tempo, sta gradualmente venendo sostituito da nuovi linguaggi di markup.
Per prima cosa definiamo una struct
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) // Serializzazione XML
func MarshalIndent(v any, prefix, indent string) ([]byte, error) // Formattazione
func Unmarshal(data []byte, v any) error // DeserializzazioneSerializzazione
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))
}Output
<Person>
<id>120</id>
<name>jack</name>
<age>18</age>
<address>usa</address>
</Person>Deserializzazione
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
}
}Output
{UserId:120 Username:jack Age:18 Address:usa}Tuttavia, il metodo tradizionale di parsing XML richiede spesso la creazione di struct, il che può essere molto macchinoso. Ora si analizzano strutture XML semplici, ma se si utilizzano strutture complesse, può diventare molto complicato. Quindi nella maggior parte dei casi useremo una libreria open source di terze parti etree per analizzare XML. Chi è interessato può approfondire: Go比较好用的解析 xml 文件的插件 etree - 掘金 (juejin.cn)。
YML
La sintassi di YAML è simile ad altri linguaggi di programmazione di alto livello e può esprimere semplicemente liste, mappe, scalari e altre forme di dati. Utilizza l'indentazione con spazi bianchi e molte caratteristiche visive, ed è particolarmente adatto per esprimere o modificare strutture di dati e vari file di configurazione. YML esiste anche in molti progetti sotto forma di file di configurazione, con una struttura di contenuto più concisa e chiara. Go ufficiale non fornisce supporto per YML, dobbiamo utilizzare un pacchetto di terze parti.
go get github.com/go-yaml/yamlMetodi Principali
func Marshal(in interface{}) (out []byte, err error) // Serializzazione
func Unmarshal(in []byte, out interface{}) (err error) // DeserializzazionePrepariamo prima una struct
type Config struct {
Database string `yaml:"database"`
Url string `yaml:"url"`
Port int `yaml:"port"`
Username string `yaml:"username"`
Password string `yaml:"password"`
}File di configurazione
database: mysql
url: 127.0.0.1
port: 3306
username: root
password: 123456Serializzazione
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))
}Output
database: oracle
url: localhost
port: 3326
username: root
password: "123456"Tuttavia, poiché yml ha di per sé una sintassi di indentazione rigorosa, non ci sono problemi di formattazione della serializzazione.
Deserializzazione
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)
}Output
{mysql 127.0.0.1 3306 root 123456}JSON
json è comunemente utilizzato nelle comunicazioni di interfacce in stile Restful. Rispetto a xml, ha dimensioni più leggere e costi di apprendimento più bassi, il che lo ha reso il formato di scambio dati principale nel campo web.
In Go, il pacchetto encoding/json fornisce le funzioni corrispondenti per eseguire la serializzazione e deserializzazione JSON. Le funzioni principali utilizzate sono le seguenti.
func Marshal(v any) ([]byte, error) // Serializza un oggetto Go in una stringa JSON
func Unmarshal(data []byte, v any) error // Deserializza una stringa JSON in un oggetto GoPer prima cosa definiamo una struct
type Person struct {
UserId string
Username string
Age int
Address string
}Serializzazione
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))
}Risultato
{ "UserId": "120", "Username": "jack", "Age": 18, "Address": "usa" }Rinominare i Campi
Possiamo ottenere l'effetto di rinominare i campi attraverso i tag delle struct.
type Person struct {
UserId string `json:"id"`
Username string `json:"name"`
Age int `json:"age"`
Address string `json:"address"`
}A questo punto l'output è
{ "id": "1202", "name": "jack", "age": 19, "address": "USA" }Indentazione
Di default la serializzazione non ha alcuna indentazione, questo per ridurre il consumo di spazio durante la trasmissione, ma questo non favorisce l'osservazione umana. In alcuni casi dobbiamo serializzarlo in una forma osservabile dall'essere umano. Per fare ciò, basta cambiare funzione.
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))
}Output come segue
{
"id": "1202",
"name": "jack",
"age": 19,
"address": "USA"
}Deserializzazione
Durante la deserializzazione, bisogna fare attenzione che se la struct ha tag json, il nome del campo ha priorità rispetto al tag json, altrimenti viene utilizzato il nome della proprietà della struct.
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)
}Output
{UserId:120 Username:jack Age:18 Address:usa}Protocol Buffers
Protocol è un linguaggio neutrale, protocollo neutrale, meccanismo di serializzazione di dati strutturati estensibili open source di Google nel 2008. Rispetto alle tre precedenti è più leggero e più veloce durante l'apertura e la chiusura dei pacchetti, utilizzato principalmente nel campo delle comunicazioni RPC. Per la spiegazione di Protobuf si può andare a Protobuf.
Installare le dipendenze
go get github.com/golang/protobuf/protoFile person.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;
}Dopo aver generato il file
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)// Serializzazione
if err != nil {
fmt.Println(err)
return
}
temp := &p.Person{}
fmt.Println("proto buffer len: ", len(data), "bytes:", data)
err = proto.Unmarshal(data, temp)// Deserializzazione
if err != nil {
fmt.Println(err)
return
}
fmt.Println(temp)
}Output
proto buffer len: 9 bytes: [10 3 119 121 104 16 12 24 1]
name:"wyh" age:12 gender:FE_MAILTuttavia, di solito non si esegue la serializzazione manualmente, il compilatore protoc può generare il codice sorgente nel linguaggio corrispondente in base al file proto definito da noi.
