Manipulando JSON em Go

Manipulando JSON em Go

Durante a sua jornada como desenvolvedor em Go, você vai se deparar com alguns projetos que fazem o uso de arquivos .json.

Nesta lição, você irá aprender tudo o que precisa saber sobre eles, desde o que eles são, como criá-los, e utilizar em seus projetos 😀

Criando seu projeto de testes

Dentro da pasta JornadaGoLang, nós iremos criar uma nova pasta chamada de 16-manipulando-json, onde dentro dela, vamos criar o nosso arquivo main.go:

package main

func main() {

}

Feito isso, vamos conhecer um pouco mais sobre os arquivos .json 😉

O que são arquivos JSON?

JSON é um acrônimo para JavaScript Object Notation, que nada mais é do que um arquivo de notação feito em um formato leve de intercâmbio de dados.

Arquivos JSON são fáceis de ler e de se escrever para nós humanos, ao mesmo tempo que é simples de ser interpretado pelas máquinas.

Tais arquivos são amplamente utilizados para o armazenamento e a transmissão de dados entre sistemas, especialmente em aplicações web e APIs.

Ou seja, arquivos .JSON pode estar localizados localmente na sua máquina, como também podem ser passados entre sistemas web (por meio de requisições http e afins).

Como funciona a estrutura de um arquivo JSON?

O JSON é baseado em dois tipos de estruturas:

  • Objetos: Representados por um conjuntos de pares chave-valor, delimitados por {}.
  • Arrays: Representados por listas ordenadas de valores, delimitadas por [].

Vejamos um exemplo bem simples de um JSON representado por objetos:

{
  "nome": "João",
  "idade": 30,
  "casado": false,
  "hobbies": ["futebol", "leitura", "música"],
  "endereco": {
    "rua": "Av. Brasil",
    "cidade": "São Paulo",
    "cep": "01000-000"
  }
}

Note que o formato JSON é um formato fácil de ser lido, onde representam objetos no GoLang.

Creio que nesta altura do campeonato você já tenha mais ou menos uma ideia de como processá-los, não é verdade? Já que eles são bem similares aos maps😉 

Características de um JSON

Como vimos anteriormente, um JSON nada mais é do que uma composição de objetos organizados em chave e valor.

Com relação às suas chaves e valores, ele suporta diversos tipos básicos, tais como:

  • string,
  • int,
  • float,
  • bool,
  • complex
  • e etc...

Além disso, o JSON pode conter objetos aninhados, como você mesmo viu no exemplo anterior:

"hobbies": ["futebol", "leitura", "música"]

Ah, e não se esqueça de utilizar vírgula para separar seus elementos, e dois pontos para associar chave-valor 😄

Vejamos um outro exemplo de um arquivo JSON que começa com uma lista de objetos:

[
  {
    "nome": "Alice",
    "idade": 25
  },
  {
    "nome": "Bruno",
    "idade": 30
  },
  {
    "nome": "Carla",
    "idade": 22
  }
]

Perceba que ele começa com [], em vez de {} pois se trata de uma lista.

Onde o JSON pode ser usado?

Atualmente, o formato JSON pode ter diversas aplicações no mundo da tecnologia.

Podemos usá-lo para trocar informações entre uma aplicação front-end e back-end por meio de APIs RESTful.

Ele também pode ser usados para o armazenamento de preferências do sistema e configurações de software (pode ser uma alternativa aos arquivos .env).

Além disso, podemos usar arquivos JSON para a internacionalização da nossa aplicação (famosa tradução).

Agora que você já sabe tudo o que precisa saber sobre a anotação JSON, vamos aprender a manipulá-lo usando GoLang 🙃

Importando a biblioteca encoding/json dentro do seu projeto

Por padrão, o GoLang já conta com uma biblioteca que é capaz de fazer a interpretação de um JSON.

Essa biblioteca é chamada de enconding/json, e pode ser incluída facilmente dentro do seu projeto da seguinte forma:

import "encoding/json"

Ou, caso você já tenha outras bibliotecas importadas, basta usar assim:

import (
        "..."
	"encoding/json"
)

Quando trabalhamos com essa biblioteca, os dados que foram lidos de um JSON podem ser representados por:

  • Structs (para dados estruturados)
  • Mapas (map[string]interface{}) (para dados dinâmicos)
  • Slices ([]interface{}) (para listas)

Dito isso, vamos aprender a criar um JSON 😉

Criando um JSON a partir de uma struct

No GoLang, a melhor prática para se criar um JSON, é a partir do mapeamento de uma struct, observe:

package main

import (
	"encoding/json"
	"fmt"
)

type Pessoa struct {
	Nome  string `json:"nome"`
	Idade int    `json:"idade"`
}

func main() {
	// Criando uma instância da struct
	pessoa := Pessoa{Nome: "Alice", Idade: 25}

	// Convertendo a struct para JSON
	jsonBytes, err := json.Marshal(pessoa)
	if err != nil {
		fmt.Println("Erro ao converter para JSON:", err)
		return
	}

	// Convertendo bytes para string e imprimindo
	fmt.Println(string(jsonBytes))
}

Observe que estamos convertendo os dados da struct Pessoa para a anotação JSON usando a função Marshal.

Lembrando que a tag json:"nome" existente no struct define o nome do campo no JSON. Isso significa dizer que se você quer criar uma struct que seja representada em JSON, você sempre deverá fazer o uso dessas tags.

É importante ressaltar que a função Marshal, converte os dados de uma struct para a anotação JSON, tanto é que se você der uma olhada no terminal, verá algo como:

{"nome":"Alice","idade":25}

O que indica que os nossos dados estão salvos em uma estrutura JSON.

Para salvar essas informações em um arquivo .json, basta seguir as explicações repassadas durante a lição que fala sobre manipulação de arquivos em Go.

Convertendo um JSON para uma struct (deserialização)

Deserialização nada mais é do que o ato de pegar uma anotação em JSON e convertê-la de volta para uma struct, map ou array, para que então, possamos ter acesso aos seus dados.

No GoLang, podemos fazer isso usando a função Unmarshal da seguinte forma:

package main

import (
	"encoding/json"
	"fmt"
)

type Pessoa struct {
	Nome  string `json:"nome"`
	Idade int    `json:"idade"`
}

func main() {
	// JSON de entrada (simulação de um arquivo ou API)
	jsonData := `{"nome":"Alice","idade":25}`

	// Criando uma variável para armazenar os dados
	var pessoa Pessoa

	// Convertendo JSON para struct
	err := json.Unmarshal([]byte(jsonData), &pessoa)
	if err != nil {
		fmt.Println("Erro ao decodificar JSON:", err)
		return
	}

	// Exibindo os dados
	fmt.Println("Nome:", pessoa.Nome)
	fmt.Println("Idade:", pessoa.Idade)
}

A saída será:

Nome: Alice
Idade: 25

Gravando um JSON em um Arquivo

O processo de gravação de um JSON em um arquivo, pode ser obtido por meio da utilização da biblioteca os, observe:

package main

import (
	"encoding/json"
	"fmt"
	"os"
)

type Pessoa struct {
	Nome  string `json:"nome"`
	Idade int    `json:"idade"`
}

func main() {
	// Criando uma struct
	pessoa := Pessoa{Nome: "Bruno", Idade: 30}

	// Criando o arquivo
	file, err := os.Create("pessoa.json")
	if err != nil {
		fmt.Println("Erro ao criar o arquivo:", err)
		return
	}
	defer file.Close()

	// Criando o encoder e escrevendo no arquivo
	encoder := json.NewEncoder(file)
	err = encoder.Encode(pessoa)
	if err != nil {
		fmt.Println("Erro ao escrever JSON:", err)
		return
	}

	fmt.Println("Arquivo JSON salvo com sucesso!")
}

No exemplo acima, será criado um arquivo chamado pessoa.json, dentro da mesma pasta onde está localizado seu arquivo main.go.

A função json.NewEncoder(file).Encode(pessoa) é responsável por realizar a gravação desses dados.

Lendo um JSON de um Arquivo

Agora, vamos aprender a realizar a gravação de um arquivo JSON 👾

package main

import (
	"encoding/json"
	"fmt"
	"os"
)

type Pessoa struct {
	Nome  string `json:"nome"`
	Idade int    `json:"idade"`
}

func main() {
	// Abrindo o arquivo
	file, err := os.Open("pessoa.json")
	if err != nil {
		fmt.Println("Erro ao abrir o arquivo:", err)
		return
	}
	defer file.Close()

	// Criando uma variável para armazenar os dados
	var pessoa Pessoa

	// Criando um decoder e lendo os dados do arquivo
	decoder := json.NewDecoder(file)
	err = decoder.Decode(&pessoa)
	if err != nil {
		fmt.Println("Erro ao ler JSON:", err)
		return
	}

	// Exibindo os dados
	fmt.Println("Nome:", pessoa.Nome)
	fmt.Println("Idade:", pessoa.Idade)
}

Note que usamos a biblioteca os para salvar este arquivo em conjunto com um nome especificado.

Manipulando JSONs dinâmicamente

Anteriormente, você aprendeu a manipular arquivos JSON nas quais seus índices (chaves) já eram conhecidos.

Porém, nem sempre você vai saber de todos os campos que um JSON poderá retornar.

Em casos como estes, se o JSON for desconhecido ou tiver campos variáveis, você pode usar um map[string]interface{} para fazer a leitura automática, observe:

package main

import (
	"encoding/json"
	"fmt"
)

func main() {
	// JSON desconhecido
	jsonData := `{"nome":"Carlos","idade":40,"profissao":"Engenheiro"}`

	// Criando um mapa para armazenar os dados
	var data map[string]interface{}

	// Convertendo JSON para mapa
	err := json.Unmarshal([]byte(jsonData), &data)
	if err != nil {
		fmt.Println("Erro ao decodificar JSON:", err)
		return
	}

	// Exibindo os valores
	fmt.Println("Nome:", data["nome"])
	fmt.Println("Idade:", data["idade"])
	fmt.Println("Profissão:", data["profissao"])
}

Note que o comando map[string]interface{} permite armazenar qualquer tipo de dado em JSON.

E como você já deve saber, basta usar data["chave"] para acessar os valores do map 😉

Mas e se o JSON for uma lista de objetos desconhecidos?

Bem, neste caso, é recomendável que você faça o uso de um slice ([]map[string]interface{}) para representar cada item de um map:

package main

import (
	"encoding/json"
	"fmt"
)

func main() {
	// JSON com uma lista de objetos
	jsonData := `[{"nome":"Carlos","idade":40,"profissao":"Engenheiro"},
	              {"nome":"Maria","idade":35,"profissao":"Médica"},
	              {"nome":"João","idade":28}]`

	// Criando um slice de maps
	var pessoas []map[string]interface{}

	// Convertendo JSON para slice de mapas
	err := json.Unmarshal([]byte(jsonData), &pessoas)
	if err != nil {
		fmt.Println("Erro ao decodificar JSON:", err)
		return
	}

	// Iterando sobre os objetos do JSON
	for i, pessoa := range pessoas {
		fmt.Println("Pessoa", i+1)
		fmt.Println("Nome:", pessoa["nome"])
		fmt.Println("Idade:", pessoa["idade"])

		// Profissão pode não existir em todos os objetos
		if profissao, existe := pessoa["profissao"]; existe {
			fmt.Println("Profissão:", profissao)
		} else {
			fmt.Println("Profissão: Não informada")
		}
		fmt.Println("----------------------")
	}
}

A saída será:

Pessoa 1
Nome: Carlos
Idade: 40
Profissão: Engenheiro
----------------------
Pessoa 2
Nome: Maria
Idade: 35
Profissão: Médica
----------------------
Pessoa 3
Nome: João
Idade: 28
Profissão: Não informada
----------------------
  • Criamos um slice de map[string]interface{} para lidar com listas.
  • Utilizamos json.Unmarshal() para converter o JSON para Go.
  • Utilizamos json.Unmarshal() para converter o JSON para Go.
  • Iteramos sobre o slice para acessar cada objeto.
  • Verificamos se a chave "profissao" existe antes de exibi-la.

Trabalhando com Listas em JSON (Array)

Caso você tenha certeza de que o JSON é uma lista, você pode fazer o uso de um slice de structs da seguinte forma:

package main

import (
	"encoding/json"
	"fmt"
)

type Pessoa struct {
	Nome  string `json:"nome"`
	Idade int    `json:"idade"`
}

func main() {
	// JSON de uma lista de pessoas
	jsonData := `[{"nome":"Ana","idade":20},{"nome":"Pedro","idade":35}]`

	// Criando um slice para armazenar os dados
	var pessoas []Pessoa

	// Convertendo JSON para slice de structs
	err := json.Unmarshal([]byte(jsonData), &pessoas)
	if err != nil {
		fmt.Println("Erro ao decodificar JSON:", err)
		return
	}

	// Exibindo os dados
	for _, pessoa := range pessoas {
		fmt.Println("Nome:", pessoa.Nome, "| Idade:", pessoa.Idade)
	}
}

A saída será:

Nome: Ana | Idade: 20
Nome: Pedro | Idade: 35

Reconhecendo se o JSON é um array ou um objeto

Antes de fazer o parse (ato de transformar um JSON em um array, map ou slice) de um JSON em Go, podemos verificar se ele é uma lista (array) ou um objeto (map).

Isso é útil para garantir que estamos manipulando os dados corretamente sem gerar erros.

Atualmente, nós podemos fazer isso de duas formas:

  • Usando json.Unmarshal() com interface{} e type assertion (switch)
  • Usando json.Decoder().Token() para analisar a estrutura

Método 1: json.Unmarshal() com Type Assertion

No comando abaixo estamos usando o Unmarshal com Type Assertion para identificar um JSON:

package main

import (
	"encoding/json"
	"fmt"
)

func detectarJSON(jsonData []byte) {
	// Criamos uma variável do tipo `interface{}` para armazenar os dados
	var temp interface{}

	// Fazemos o parse do JSON
	err := json.Unmarshal(jsonData, &temp)
	if err != nil {
		fmt.Println("Erro ao fazer o parse do JSON:", err)
		return
	}

	// Verificamos o tipo da estrutura usando type assertion
	switch temp.(type) {
	case []interface{}:
		fmt.Println("O JSON é uma LISTA (array).")
	case map[string]interface{}:
		fmt.Println("O JSON é um OBJETO (map).")
	default:
		fmt.Println("Formato desconhecido.")
	}
}

func main() {
	// JSON de exemplo (lista)
	jsonLista := []byte(`[{"nome": "Carlos", "idade": 40}, {"nome": "Maria", "idade": 35}]`)

	// JSON de exemplo (objeto)
	jsonObjeto := []byte(`{"empresa": "TechCorp", "fundacao": 1999}`)

	fmt.Println("Detectando JSON Lista:")
	detectarJSON(jsonLista)

	fmt.Println("\nDetectando JSON Objeto:")
	detectarJSON(jsonObjeto)
}
  • Usamos json.Unmarshal() para converter o JSON em interface{}.
  • Em seguida, usamos um switch para verificar se o tipo resultante é []interface{} (lista) ou map[string]interface{} (objeto).
  • Se o JSON não for nenhum dos dois, exibimos "Formato desconhecido".

Método 2: Usando json.Decoder().Token()

Outra forma de verificar a estrutura do JSON sem carregar todos os dados na memória é usar o primeiro token lido pelo json.Decoder():

package main

import (
	"encoding/json"
	"fmt"
	"strings"
)

func detectarJSONComDecoder(jsonData string) {
	// Criamos um decoder para processar o JSON em stream
	decoder := json.NewDecoder(strings.NewReader(jsonData))

	// Pegamos o primeiro token do JSON
	token, err := decoder.Token()
	if err != nil {
		fmt.Println("Erro ao ler JSON:", err)
		return
	}

	// Verificamos se o primeiro token é um '[' (lista) ou '{' (objeto)
	switch token {
	case json.Delim('['):
		fmt.Println("O JSON é uma LISTA (array).")
	case json.Delim('{'):
		fmt.Println("O JSON é um OBJETO (map).")
	default:
		fmt.Println("Formato desconhecido.")
	}
}

func main() {
	// JSON de exemplo (lista)
	jsonLista := `[{"nome": "Carlos", "idade": 40}, {"nome": "Maria", "idade": 35}]`

	// JSON de exemplo (objeto)
	jsonObjeto := `{"empresa": "TechCorp", "fundacao": 1999}`

	fmt.Println("Detectando JSON Lista:")
	detectarJSONComDecoder(jsonLista)

	fmt.Println("\nDetectando JSON Objeto:")
	detectarJSONComDecoder(jsonObjeto)
}
  • O json.Decoder().Token() lê apenas o primeiro caractere significativo do JSON.
  • Se for '[', sabemos que o JSON é uma lista.
  • Se for '{', sabemos que é um objeto.

Esse método é mais eficiente, pois não precisa carregar o JSON inteiro na memória.

Verificando se um JSON é válido

Haverão momentos em que por obra de erros de softwares, ou de desenvolvedores desapercebidos, um JSON pode ser inválido.

Pode ser inválido por diversos motivos, tais como erros de software ou desenvolvedores que se esqueceram de fechar uma tag, e que por conta disso, ocasionou erros na leitura desses arquivos.

Por exemplo:

[
  {
    "nome: "Alice",
    "idade": 25
  },
  {
    "nome": "Bruno",
    "idade": 30
  }
  {
    "nome": "Carla",
    "idade": 22
  }

O JSON acima tem 3 problemas:

  • A primeira chave nome, não está fechada corretamente: "nome: "Alice",
  • O segundo objeto da lista não possuí uma vírgula }
  • O final do arquivo carece de um ], o que indica que a lista não foi fechada corretamente.

Durante a sua jornada como desenvolvedor, é bem provável que você vá encontrar muitos erros assim, o que pode ocasionar um erro de leitura.

E foi pensando nisso, que existe a função valid() da biblioteca encoding/json, que é capaz de validar se o JSON é válido ou não:

package main

import (
	"encoding/json"
	"fmt"
)

func verificarJSON(jsonData []byte) {
	if json.Valid(jsonData) {
		fmt.Println("✅ O JSON é válido!")
	} else {
		fmt.Println("❌ O JSON é inválido!")
	}
}

func main() {
	// JSON válido (Objeto)
	jsonValido := []byte(`{"nome": "Carlos", "idade": 30}`)

	// JSON válido (Lista)
	jsonListaValida := []byte(`[{"nome": "Ana"}, {"nome": "Pedro"}]`)

	// JSON inválido (chave sem aspas e falta de vírgula)
	jsonInvalido := []byte(`{nome: "Carlos" "idade": 30}`)

	// JSON inválido (falta de fechamento do colchete)
	jsonListaInvalida := []byte(`[{"nome": "Ana", "idade": 25}`)

	fmt.Println("Verificando JSON Objeto:")
	verificarJSON(jsonValido)

	fmt.Println("\nVerificando JSON Lista:")
	verificarJSON(jsonListaValida)

	fmt.Println("\nVerificando JSON Inválido (Objeto):")
	verificarJSON(jsonInvalido)

	fmt.Println("\nVerificando JSON Inválido (Lista):")
	verificarJSON(jsonListaInvalida)
}

O comando json.Valid(jsonData) irá retornar true se o JSON estiver corretamente formatado.

Caso o JSON tenha erros, a função retorna false, permitindo que você trate isso antes de tentar fazer json.Unmarshal().

Adicionando, atualizando e removendo valores de um JSON

Nem sempre, você fará o uso de um JSON somente para consultar (ler) seus valores. Existirão casos, em você você vai precisar adicionar, atualizar e até mesmo remover valores de um JSON.

Em casos como estes, podemos usar slices ([]) e mapas (map[string]interface{}) antes de salvar o JSON atualizado no arquivo.

Veremos abaixo um passo a passo de como isso pode ser feito 🥳

📌 Estrutura do Exemplo

Vamos trabalhar com um JSON representando uma lista de pessoas, onde cada pessoa tem nome, idade e, opcionalmente uma profissao.

[
  {"nome": "Carlos", "idade": 40, "profissao": "Engenheiro"},
  {"nome": "Maria", "idade": 35, "profissao": "Médica"},
  {"nome": "João", "idade": 28}
]

Considerado que essa anotação está salva em uma arquivo chamado pessoas.json que existe na mesma pasta de onde está seu arquivo main.go, podemos prosseguir para a leitura do mesmo.

📂 1. Abrindo e Lendo um JSON de um Arquivo

Antes de manipular os dados, precisamos carregá-los de um arquivo:

package main

import (
	"encoding/json"
	"fmt"
	"os"
)

func lerArquivoJSON(caminho string) ([]map[string]interface{}, error) {
	// Abrindo o arquivo
	file, err := os.Open(caminho)
	if err != nil {
		return nil, err
	}
	defer file.Close()

	// Criando um slice de mapas para armazenar os dados
	var pessoas []map[string]interface{}

	// Decodificando o JSON para o slice
	decoder := json.NewDecoder(file)
	err = decoder.Decode(&pessoas)
	if err != nil {
		return nil, err
	}

	return pessoas, nil
}

No momento, criamos apenas uma função chamada lerArquivoJSON que recebe um caminho, e retorna um slice de mapas.

📝 2. Adicionando um Novo Objeto ao JSON

Com o arquivo aberto, você pode criar uma função especifica que será responsável por adicionar novas pessoas ao JSON:

func adicionarPessoa(pessoas *[]map[string]interface{}, nome string, idade int, profissao string) {
	novaPessoa := map[string]interface{}{
		"nome": nome,
		"idade": idade,
	}
	// Adicionando a profissão se não for vazia
	if profissao != "" {
		novaPessoa["profissao"] = profissao
	}

	// Adicionando ao slice
	*pessoas = append(*pessoas, novaPessoa)
}

Note que no exemplo acima, criamos um map[string]interface{} para armazenar os novos dados.

Caso a profissao for informada, ela é adicionada de forma automática dentro do mapa.

Observe também que fizemos o uso do append() para incluir a nova pessoa na lista.

✏️ 3. Atualizando um Objeto no JSON

O processo de atualização também é bem fácil, basta ter o map em mãos, percorrer a lista, encontrar o item e fazer a modificação, observe:

func atualizarPessoa(pessoas *[]map[string]interface{}, nome string, novaIdade int, novaProfissao string) bool {
	for i, pessoa := range *pessoas {
		if pessoa["nome"] == nome {
			(*pessoas)[i]["idade"] = novaIdade
			if novaProfissao != "" {
				(*pessoas)[i]["profissao"] = novaProfissao
			}
			return true
		}
	}
	return false
}

No exemplo acima, se encontrarmos o nome informado, atualizamos sua idade e profissao.

Por fim, retornamos true ou false, em caso da pessoa ter sido encontrada ou não.

❌ 4. Removendo um Objeto do JSON

Para remover um item da lista, é bem simples, tudo o que você precisa fazer é criar um novo slice sem o elemento desejado:

func removerPessoa(pessoas *[]map[string]interface{}, nome string) bool {
	for i, pessoa := range *pessoas {
		if pessoa["nome"] == nome {
			// Removendo o item do slice
			*pessoas = append((*pessoas)[:i], (*pessoas)[i+1:]...)
			return true
		}
	}
	return false
}

No exemplo acima, buscamos um índice da pessoa a ser removida.

Em seguida usamos um slicing (append(slice[:i], slice[i+1:]...)) para excluir tal item da lista.

💾 5. Salvando as Alterações no Arquivo

Por fim, não podemos nos esquecer de criar a última função, e não menos importante, que será responsável por salvar nossos dados em um JSON:

func salvarArquivoJSON(caminho string, pessoas []map[string]interface{}) error {
	// Criando o arquivo (ou sobrescrevendo)
	file, err := os.Create(caminho)
	if err != nil {
		return err
	}
	defer file.Close()

	// Criando um encoder para escrever o JSON no arquivo
	encoder := json.NewEncoder(file)
	encoder.SetIndent("", "  ") // Indentação para melhor visualização

	return encoder.Encode(pessoas)
}

Lembrando que usamos o comando json.NewEncoder(file).Encode(pessoas) para salvar os dados formatados.

🚀 6. Testando Todas as Funcionalidades

Agora que já criamos todas as funções necessárias, vamos criar a nossa função main() onde vai conter todas as chamadas de ler, adicionar, atualizar, remover e salvar o JSON.

package main

import (
	"encoding/json"
	"fmt"
	"os"
)

func lerArquivoJSON(caminho string) ([]map[string]interface{}, error) {
	file, err := os.Open(caminho)
	if err != nil {
		return nil, err
	}
	defer file.Close()

	var pessoas []map[string]interface{}
	decoder := json.NewDecoder(file)
	err = decoder.Decode(&pessoas)
	if err != nil {
		return nil, err
	}
	return pessoas, nil
}

func adicionarPessoa(pessoas *[]map[string]interface{}, nome string, idade int, profissao string) {
	novaPessoa := map[string]interface{}{
		"nome":  nome,
		"idade": idade,
	}
	if profissao != "" {
		novaPessoa["profissao"] = profissao
	}
	*pessoas = append(*pessoas, novaPessoa)
}

func atualizarPessoa(pessoas *[]map[string]interface{}, nome string, novaIdade int, novaProfissao string) bool {
	for i, pessoa := range *pessoas {
		if pessoa["nome"] == nome {
			(*pessoas)[i]["idade"] = novaIdade
			if novaProfissao != "" {
				(*pessoas)[i]["profissao"] = novaProfissao
			}
			return true
		}
	}
	return false
}

func removerPessoa(pessoas *[]map[string]interface{}, nome string) bool {
	for i, pessoa := range *pessoas {
		if pessoa["nome"] == nome {
			*pessoas = append((*pessoas)[:i], (*pessoas)[i+1:]...)
			return true
		}
	}
	return false
}

func salvarArquivoJSON(caminho string, pessoas []map[string]interface{}) error {
	file, err := os.Create(caminho)
	if err != nil {
		return err
	}
	defer file.Close()

	encoder := json.NewEncoder(file)
	encoder.SetIndent("", "  ") // Indentação para melhor visualização
	return encoder.Encode(pessoas)
}

func main() {
	// Caminho do arquivo JSON
	caminho := "pessoas.json"

	// Ler o arquivo JSON
	pessoas, err := lerArquivoJSON(caminho)
	if err != nil {
		fmt.Println("Erro ao ler JSON:", err)
		return
	}

	// Adicionar uma nova pessoa
	adicionarPessoa(&pessoas, "Lucas", 27, "Designer")

	// Atualizar uma pessoa
	atualizarPessoa(&pessoas, "Carlos", 41, "Gerente de Engenharia")

	// Remover uma pessoa
	removerPessoa(&pessoas, "Maria")

	// Salvar alterações no arquivo
	err = salvarArquivoJSON(caminho, pessoas)
	if err != nil {
		fmt.Println("Erro ao salvar JSON:", err)
		return
	}

	fmt.Println("Alterações salvas com sucesso!")
}

Incrível, não acha?

Repositório da lição

Todos os arquivos relacionados com esta lição, podem ser encontrados nos seguintes repositórios abaixo:

Conclusão

Nesta lição, você aprendeu a manipular arquivos JSON de diversas formas diferentes:

✅ Como converter um JSON para struct

✅ Como salvar e ler JSONs em arquivos

✅ Como manipular JSONs dinâmicos com map[string]interface{}

✅ Como lidar com listas de JSON

✅ Como adicionar novos itens a um JSON

✅ Como atualizar valores existentes

✅ Como remover um objeto de uma lista JSON

✅ Como salvar o JSON atualizado no arquivo

Assuntos suficientes para que você possa dominar a manipulação de JSONs em qualquer projeto! 🚀 

Até a próxima lição.

Criadores de Conteúdo

Foto do William Lima
William Lima
Fundador da Micilini

Inventor nato, escreve conteudos de programação para o portal da micilini.

Torne-se um MIC 🤖

Mais de 100 mic's já estão conectados na plataforma.