Skip to content

Codificação e Decodificação em Go

Na era digital atual, os formatos de dados independentes de linguagem mais comumente usados são xml, Yaml, json e protobuf. Go também suporta operações relacionadas a esses formatos de dados. Abaixo está uma tabela comparativa.

NomeXMLYAMLJSONProtocol Buffers
Estrutura de DadosComplexaMais SimplesSimplesMais Complexa
Método de ArmazenamentoTextoTextoTextoBinário
Tamanho de ArmazenamentoGrandeMédioMédioPequeno
Eficiência de AnáliseLentaMédiaMédiaRápida
Suporte de LinguagemMuitosMuitosMuitosVários
Dificuldade de DesenvolvimentoTrabalhosaMais SimplesSimplesSimples
Custo de AprendizadoBaixoBaixoBaixoBaixo
Escopo de AplicaçãoTroca de DadosArquivo de ConfiguraçãoTroca de DadosTroca de Dados

TIP

Em Go, se você deseja serializar e desserializar uma struct, os campos devem ser exportados, ou seja, a primeira letra deve ser maiúscula.

Além disso, o TOML está começando a se tornar popular, com sintaxe semelhante a uma melhoria do .ini. Se estiver interessado, você pode aprender mais em TOML: A Linguagem de Configuração Mínima e Clara de Tom.

XML

xml, também conhecido como eXtensible Markup Language, é um formato usado para armazenar dados, originado na década de 1960, sendo o mais antigo entre os formatos de dados mencionados acima. Seu uso é muito amplo, podendo ser utilizado para transmissão de rede, troca de dados, arquivos de configuração, armazenamento de dados, etc. No entanto, com o passar do tempo, está sendo gradualmente substituído por novas linguagens de marcação.

Primeiro, definimos uma 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) // Serialização XML

func MarshalIndent(v any, prefix, indent string) ([]byte, error) // Formatação

func Unmarshal(data []byte, v any) error // Desserialização

Serialização

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

Saída:

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

Desserialização

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

Saída:

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

No entanto, o método tradicional de análise XML frequentemente requer a criação de structs, o que pode ser muito trabalhoso. Se a análise for de estruturas XML simples, não há problema, mas se forem estruturas complexas, pode se tornar muito complicado. Por isso, a maioria das pessoas usa uma biblioteca de terceiros chamada etree para analisar XML. Se estiver interessado, você pode aprender mais por conta própria: Plugin etree útil para análise de arquivos XML em Go - Juejin (juejin.cn).

YML

A sintaxe do YAML é semelhante a outras linguagens de programação de alto nível e pode expressar facilmente listas, mapas, escalares e outras formas de dados. Usa indentação com espaços em branco e depende muito da aparência, sendo especialmente adequado para expressar ou editar estruturas de dados e vários arquivos de configuração. O YML também existe em muitos projetos na forma de arquivos de configuração, com uma estrutura de conteúdo mais concisa e clara. O Go oficial não fornece suporte para YML, então precisamos usar um pacote de terceiros.

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

Métodos Principais

go
func Marshal(in interface{}) (out []byte, err error) // Serialização

func Unmarshal(in []byte, out interface{}) (err error) // Desserialização

Primeiro, preparamos uma 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"`
}

Arquivo de configuração:

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

Serialização

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

Saída:

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

No entanto, como o yml possui uma sintaxe de indentação rigorosa, não há problemas de formatação de serialização.

Desserialização

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

Saída:

{mysql 127.0.0.1 3306 root 123456}

JSON

O json é frequentemente usado em comunicações de interfaces de API no estilo Restful. Em comparação com o xml, seu tamanho mais leve e baixo custo de aprendizado o tornaram o formato principal de troca de dados na área web.

Em Go, o pacote encoding/json fornece funções correspondentes para realizar serialização e desserialização JSON. As principais funções usadas são as seguintes:

go
func Marshal(v any) ([]byte, error) // Serializa um objeto Go para uma string JSON

func Unmarshal(data []byte, v any) error // Desserializa uma string JSON para um objeto Go

Primeiro, definimos uma struct:

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

Serialização

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

Resultado:

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

Renomeação de Campos

Podemos usar tags de struct para alcançar o efeito de renomeação.

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

Neste caso, a saída será:

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

Indentação

Por padrão, a serialização não possui nenhuma indentação, isso é feito para reduzir o consumo de espaço durante a transmissão, mas não é favorável à observação humana. Em alguns casos, precisamos serializá-lo em uma forma que os humanos possam observar. Para isso, basta usar uma função diferente:

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

Saída:

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

Desserialização

Ao desserializar, é importante notar que se a struct tiver uma tag json, o nome do campo terá prioridade na tag json, caso contrário, usará o nome da propriedade da 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)
}

Saída:

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

Protocol Buffers

Protocol Buffers é uma linguagem neutra, independente de protocolo e extensível para serialização de dados estruturados, open-source pela Google em 2008. É mais leve que os três mencionados acima e mais rápido na serialização e desserialização, sendo muito utilizado em comunicações RPC. Para mais informações sobre Protobuf, consulte Protobuf.

Instale a dependência:

go get github.com/golang/protobuf/proto

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

Após gerar o arquivo:

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)// Serialização
  if err != nil {
    fmt.Println(err)
    return
  }
  temp := &p.Person{}
  fmt.Println("proto buffer len: ", len(data), "bytes:", data)
  err = proto.Unmarshal(data, temp)// Desserialização
  if err != nil {
    fmt.Println(err)
    return
  }
  fmt.Println(temp)
}

Saída:

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

Normalmente, não fazemos a serialização manualmente. O compilador protoc pode gerar o código-fonte na linguagem correspondente com base no arquivo proto que definimos.

Golang por www.golangdev.cn edit