Skip to content

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.

NomeXMLYAMLJSONProtocol Buffers
Struttura DatiComplessaPiuttosto SempliceSemplicePiuttosto Complessa
Modalità di SalvataggioTestoTestoTestoBinario
Dimensione SalvataggioGrandeMediaMediaPiccola
Efficienza ParsingLentaMediaMediaVeloce
Supporto LinguaggiMoltissimiMoltiMoltissimiAbbastanza
Difficoltà SviluppoLaboriosoPiuttosto SempliceSempliceSemplice
Costo ApprendimentoBassoBassoBassoBasso
Ambito di ApplicazioneScambio DatiFile di ConfigurazioneScambio DatiScambio 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

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) // Serializzazione XML

func MarshalIndent(v any, prefix, indent string) ([]byte, error) // Formattazione

func Unmarshal(data []byte, v any) error // Deserializzazione

Serializzazione

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))
}

Output

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

Deserializzazione

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

Output

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

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

Metodi Principali

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

func Unmarshal(in []byte, out interface{}) (err error) // Deserializzazione

Prepariamo prima una struct

go
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

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

Serializzazione

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))
}

Output

yaml
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

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)
}

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.

go
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 Go

Per prima cosa definiamo una struct

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

Serializzazione

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))
}

Risultato

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

Rinominare i Campi

Possiamo ottenere l'effetto di rinominare i campi attraverso i tag delle struct.

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

A questo punto l'output è

json
{ "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.

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))
}

Output come segue

json
{
  "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.

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)
}

Output

go
{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/proto

File 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;
}

Dopo aver generato il file

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)// 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_MAIL

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

Golang by www.golangdev.cn edit