encoding/json com Structs
Parte da seção Programação Orientada a Objetos do Journey de GO da Coddy — lição 76 de 107.
O pacote encoding/json permite converter structs Go para JSON e vice-versa. Isso é essencial para APIs, arquivos de configuração e armazenamento de dados. Tags de struct controlam como os campos são mapeados para chaves JSON.
Para converter uma struct para JSON, use json.Marshal:
type User struct {
Name string `json:"name"`
Email string `json:"email"`
Age int `json:"age"`
}
func main() {
user := User{Name: "Alice", Email: "alice@example.com", Age: 30}
data, _ := json.Marshal(user)
fmt.Println(string(data))
// {"name":"Alice","email":"alice@example.com","age":30}
}As tags de struct como `json:"name"` especificam os nomes das chaves JSON. Sem as tags, o Go usa o nome do campo diretamente. Apenas campos exportados (iniciados com letra maiúscula) são incluídos na saída JSON.
Para converter JSON de volta para uma struct, use json.Unmarshal:
jsonStr := `{"name":"Bob","email":"bob@example.com","age":25}`
var user User
json.Unmarshal([]byte(jsonStr), &user)
fmt.Println(user.Name) // BobOpções comuns de tags incluem omitempty para pular valores zero e - para ignorar um campo inteiramente:
type Product struct {
ID int `json:"id"`
Name string `json:"name"`
Price float64 `json:"price,omitempty"`
Internal string `json:"-"`
}Com omitempty, se Price for zero, ele não aparecerá no JSON. O campo Internal nunca é incluído, independentemente do seu valor.
Desafio
FácilVamos construir um sistema de catálogo de filmes que converte entre structs Go e JSON! Você praticará o marshaling de structs para JSON e o unmarshaling de JSON de volta para structs, usando struct tags para controlar os nomes dos campos JSON.
Você organizará seu código em dois arquivos:
movie.go: Defina seu modelo de dados de filme com as tags JSON apropriadas.Crie uma struct
Moviecom os seguintes campos e mapeamentos JSON:Title(string) mapeia para"title"Director(string) mapeia para"director"Year(int) mapeia para"year"Rating(float64) mapeia para"rating"comomitemptypara que filmes sem avaliação não mostrem o campo de avaliaçãoInternalID(string) deve ser totalmente excluído do JSON usando"-"
Crie uma função
ToJSON(m Movie) stringque converte um Movie para uma string JSON. Crie tambémFromJSON(jsonStr string) Movieque analisa uma string JSON de volta para uma struct Movie.main.go: Demonstre a conversão JSON em ambas as direções.Leia um tipo de operação (
encodeoudecode).Para
encode: Leia os detalhes do filme (título, diretor, ano, avaliação) em linhas separadas. Crie um Movie com um InternalID de"internal-123", converta-o para JSON e imprima o resultado.Para
decode: Leia uma string JSON, converta-a para uma struct Movie e imprima os detalhes do filme neste formato:Title: [title] Director: [director] Year: [year] Rating: [rating]Exiba a avaliação com uma casa decimal. Se a avaliação for 0 (não presente no JSON), imprima
Rating: N/Aem vez disso.
As seguintes entradas serão fornecidas:
- Linha 1: Tipo de operação (
encodeoudecode) - Para encode: título, diretor, ano, avaliação (quatro linhas)
- Para decode: uma string JSON (uma linha)
Por exemplo, dado:
encode
Inception
Christopher Nolan
2010
8.8Sua saída deve ser:
{"title":"Inception","director":"Christopher Nolan","year":2010,"rating":8.8}Observe que InternalID não aparece na saída por causa da tag "-".
E dado:
encode
The Matrix
Wachowskis
1999
0Sua saída deve ser:
{"title":"The Matrix","director":"Wachowskis","year":1999}Observe que rating é omitido porque é zero e possui omitempty.
E dado:
decode
{"title":"Interstellar","director":"Christopher Nolan","year":2014,"rating":8.6}Sua saída deve ser:
Title: Interstellar
Director: Christopher Nolan
Year: 2014
Rating: 8.6E dado:
decode
{"title":"Tenet","director":"Christopher Nolan","year":2020}Sua saída deve ser:
Title: Tenet
Director: Christopher Nolan
Year: 2020
Rating: N/AFolha de consulta
O pacote encoding/json converte structs Go para JSON e vice-versa usando json.Marshal e json.Unmarshal.
Para converter uma struct para JSON:
type User struct {
Name string `json:"name"`
Email string `json:"email"`
Age int `json:"age"`
}
user := User{Name: "Alice", Email: "alice@example.com", Age: 30}
data, _ := json.Marshal(user)
fmt.Println(string(data))
// {"name":"Alice","email":"alice@example.com","age":30}Para converter JSON de volta para uma struct:
jsonStr := `{"name":"Bob","email":"bob@example.com","age":25}`
var user User
json.Unmarshal([]byte(jsonStr), &user)
fmt.Println(user.Name) // BobAs tags de struct controlam o mapeamento de campos JSON:
`json:"name"`- mapeia o campo para a chave JSON "name"`json:"price,omitempty"`- omite o campo se o valor for zero`json:"-"`- exclui o campo do JSON inteiramente
type Product struct {
ID int `json:"id"`
Name string `json:"name"`
Price float64 `json:"price,omitempty"`
Internal string `json:"-"`
}Apenas campos exportados (iniciados com letra maiúscula) são incluídos na saída JSON.
Experimente você mesmo
package main
import (
"bufio"
"fmt"
"os"
"strconv"
"strings"
)
func main() {
reader := bufio.NewReader(os.Stdin)
// Ler o tipo de operação
operation, _ := reader.ReadString('\n')
operation = strings.TrimSpace(operation)
if operation == "encode" {
// Ler os detalhes do filme
title, _ := reader.ReadString('\n')
title = strings.TrimSpace(title)
director, _ := reader.ReadString('\n')
director = strings.TrimSpace(director)
yearStr, _ := reader.ReadString('\n')
yearStr = strings.TrimSpace(yearStr)
year, _ := strconv.Atoi(yearStr)
ratingStr, _ := reader.ReadString('\n')
ratingStr = strings.TrimSpace(ratingStr)
rating, _ := strconv.ParseFloat(ratingStr, 64)
// TODO: Criar uma struct Movie com os valores lidos
// Definir InternalID como "internal-123"
// Converter para JSON usando ToJSON e imprimir o resultado
} else if operation == "decode" {
// Ler a string JSON
jsonStr, _ := reader.ReadString('\n')
jsonStr = strings.TrimSpace(jsonStr)
// TODO: Converter JSON para Movie usando FromJSON
// Imprimir os detalhes do filme no formato exigido
// Usar fmt.Sprintf("%.1f", rating) para a formatação da avaliação
// Se rating for 0, imprimir "Rating: N/A" em vez disso
}
}
Esta lição inclui um quiz rápido. Comece a lição para respondê-lo e acompanhar seu progresso.
Todas as lições de Programação Orientada a Objetos
1Fundamentos de OOP em Go
Arquivos ExternosGo Workspace e MódulosPacotes e ImportaçõesNomes Exportados vs Não ExportadosIntrodução a OOP em GoStructs como ClassesDefinindo Métodos em StructsRecebedores de Ponteiro vs ValorInicialização de StructsFunções ConstrutorasRecapitulação - Calculadora Simples4Interfaces
Introdução a InterfacesImplementação ImplícitaInterface como ContratoInterface Vazia (any)Type AssertionType SwitchComposição de InterfacesInterfaces Stringer & ErrorRecapitulação - Calculadora de Formas7Encapsulamento
Campos Exportados vs Não ExportadosEncapsulamento em Nível de PacoteMétodos Getter e SetterOcultação de Informação em GoRecapitulação - Registros de Estudantes10Generics (Go 1.18+)
Introdução a GenericsParâmetros de TipoRestrições de TipoStructs GenéricasSolução Alternativa para Métodos GenéricosRecapitulação - Coleção Genérica2Mergulho Profundo em Tipos e Structs
Tipos Básicos e CompostosDefinições de Tipos PersonalizadosStruct TagsStructs AnônimasStructs AninhadasZero Values e PadrõesRecapitulação - Agenda de Contatos5Composição em vez de Herança
Por que Go não possui HerançaFundamentos de Incorporação de StructsPromoção de MétodosIncorporando Múltiplas StructsIncorporação vs AgregaçãoShadowing de Métodos IncorporadosRecapitulação - Hierarquia de Funcionários8Tratamento de Erros e OOP
A Interface errorTipos de Erro PersonalizadosError Wrapping (fmt.Errorf)Erros Sentinelaerrors.Is() e errors.As()Panic, Defer e RecoverRecapitulação - File Parser11Biblioteca Padrão & POO
io.Reader & io.Writersort.InterfaceInterface fmt.Stringerencoding/json com StructsInterface http.HandlerRecapitulação - Modelos de API REST3Ponteiros e Memória
Fundamentos de Ponteiros em GoPonteiros para StructsPassagem por Valor vs ReferênciaA Função new()Garbage Collection em GoRecapitulação - Construtor de Lista Encadeada6Polimorfismo em Go
Polimorfismo via InterfacesDuck Typing em GoRegras de Satisfação de InterfaceColeções PolimórficasInjeção de DependênciaRecapitulação - Processador de Pagamentos9Concorrência e OOP
Fundamentos de GoroutinesCanais e ComunicaçãoCanais com Buffer vs. Sem BufferInstrução Selectsync.Mutex e sync.RWMutexsync.WaitGroupDesign de Structs Thread-SafeRecapitulação - Worker Pool