---
id: 61f2009278a5f7366f79395da59447cf
author: lemonmint
title: Análise das mudanças de comportamento do encoding/json/v2 no Go 1.25 e da camada de compatibilidade com v1
description: Compara a correspondência sensível a maiúsculas e minúsculas, as alterações semânticas de omitempty/omitzero, a serialização de slices/mapas nil e as opções do encoding/json/v2, introduzido como recurso experimental do Go 1.25 (goexperiment.jsonv2).
language: pt
date: 2025-06-20T05:38:20.100520237Z
path: /blog/posts/go-1-25-encoding-json-v1-vs-v2-z74a36b08
category: release
---

O pacote `encoding/json` v2 do Go é uma nova implementação destinada a melhorar várias desvantagens da v1 existente (falta de consistência, comportamentos surpreendentes, problemas de desempenho). Trata-se de um recurso experimental, ativado por meio da tag de build `goexperiment.jsonv2`.

O ponto mais importante é que **, quando a v2 está ativada, a v1 passa a operar como uma camada de compatibilidade que emula o comportamento da v1 sobre a implementação da v2**. Isso é feito por meio da função `DefaultOptionsV1()` no arquivo `v2_v1.go`. Ou seja, a v2 oferece opções capazes de reproduzir perfeitamente o comportamento da v1 e, ao mesmo tempo, apresenta novos comportamentos padrão mais rigorosos e previsíveis.

Os principais objetivos da v2 são os seguintes.

1. **Melhoria da correção e da previsibilidade**: aplicar, por padrão, regras mais rigorosas (por exemplo, diferenciação de maiúsculas e minúsculas e proibição de chaves duplicadas) para reduzir comportamentos inesperados.
2. **Melhoria de desempenho**: redesenho dos mecanismos de análise e codificação para aumentar a eficiência.
3. **Maior flexibilidade e controle**: introdução de um sistema detalhado de opções que permite ao desenvolvedor controlar minuciosamente o processamento de JSON.

---

## Principais diferenças de semântica e comportamento

Com base principalmente no arquivo `v2_test.go`, as diferenças de comportamento entre v1 e v2 são organizadas a seguir, item por item.

### 1. Correspondência de nomes de campos (diferenciação de maiúsculas e minúsculas)

- **Comportamento da v1**: ao desserializar membros de objetos JSON em campos de structs Go, a correspondência **ignora maiúsculas e minúsculas (case-insensitive)**. Tanto `"FirstName"` quanto `"firstname"` são mapeados para o campo `FirstName`.
- **Comportamento da v2**: por padrão, a correspondência **diferencia maiúsculas de minúsculas (case-sensitive)**, mapeando apenas campos que coincidem exatamente.
- **Motivo da mudança**: a correspondência sem distinção de maiúsculas e minúsculas pode causar comportamentos inesperados e degradar o desempenho ao lidar com campos que não correspondem. A v2 adotou, por padrão, um comportamento mais claro e previsível.
- **Opções relacionadas**: na v2, é possível habilitar explicitamente a desconsideração de maiúsculas e minúsculas por campo, usando a opção de tag `json:"...,case:ignore"`, ou aplicá-la globalmente com `json.MatchCaseInsensitiveNames(true)`.

### 2. Alteração do significado da opção de tag `omitempty`

- **Comportamento da v1**: os campos são omitidos com base no "estado vazio" do valor Go. Nesse contexto, "estado vazio" significa `false`, `0`, ponteiros ou interfaces `nil`, e arrays, slices, mapas ou strings com comprimento zero.
- **Comportamento da v2**: os campos são omitidos com base no "estado vazio" do valor JSON codificado. Ou seja, são omitidos quando codificados como `null`, `""`, `{}` ou `[]`.
- **Motivo da mudança**: a definição da v1 depende do sistema de tipos do Go. A v2 toma como base o sistema de tipos do JSON, oferecendo um comportamento mais consistente. Por exemplo, na v1 o valor `false` de um campo do tipo `bool` é omitido, ao passo que na v2 `false` é um valor JSON não vazio e, portanto, não é omitido. A v2 acrescenta a opção `omitzero`, que pode substituir o comportamento do `omitempty` da v1 aplicado a `0` ou `false`.
- **Opções relacionadas**: para obter na v2 o mesmo comportamento da v1, use a opção `json.OmitEmptyWithLegacyDefinition(true)`.

### 3. Alteração do comportamento da opção de tag `string`

- **Comportamento da v1**: aplica-se a campos dos tipos numérico, booleano e string. O valor é novamente codificado dentro de uma string JSON (por exemplo, `int(42)` -> `"42"`). Não se aplica recursivamente a valores dentro de tipos compostos (slices, mapas etc.).
- **Comportamento da v2**: **aplica-se apenas a tipos numéricos** e é aplicada recursivamente. Ou seja, os números dentro de um slice como `[]int` também são todos codificados como strings JSON.
- **Motivo da mudança**: a principal finalidade da opção `string` é representar números como strings para evitar a perda de precisão em inteiros de 64 bits. O comportamento da v1 era limitado e pouco consistente. A v2 concentra-se nesse uso essencial e amplia o comportamento de forma recursiva, tornando-o mais útil.
- **Opções relacionadas**: a opção `json.StringifyWithLegacySemantics(true)` permite imitar o comportamento da v1.

### 4. Desserialização (marshal) de slices e mapas `nil`

- **Comportamento da v1**: slices `nil` e mapas `nil` são serializados como `null`.
- **Comportamento da v2**: por padrão, slices `nil` são serializados como `[]` (array vazio) e mapas `nil` como `{}` (objeto vazio).
- **Motivo da mudança**: `nil` é um detalhe de implementação da linguagem Go, e não é desejável expô-lo em um formato JSON independente de linguagem. `[]` e `{}` são representações mais universais de coleções vazias.
- **Opções relacionadas**: na v2, é possível serializar como `null`, como na v1, com as opções `json.FormatNilSliceAsNull(true)` ou `json.FormatNilMapAsNull(true)`.

### 5. Desserialização de arrays

- **Comportamento da v1**: ao desserializar para arrays Go (`[N]T`), não é gerado erro quando o comprimento do array JSON difere do comprimento do array Go. Se o array JSON for menor, o espaço restante é preenchido com valores zero; se for maior, o excedente é descartado.
- **Comportamento da v2**: o comprimento do array JSON deve **coincidir exatamente** com o comprimento do array Go. Caso contrário, ocorre um erro.
- **Motivo da mudança**: em Go, arrays de tamanho fixo frequentemente têm o comprimento como informação semanticamente importante. O comportamento da v1 pode causar perda silenciosa de dados. A v2 aumenta a correção com regras mais rigorosas.
- **Opções relacionadas**: a opção `json.UnmarshalArrayFromAnyLength(true)` permite imitar o comportamento da v1.

### 6. Tratamento de `time.Duration`

- **Comportamento da v1**: `time.Duration` é tratado internamente como `int64` e codificado como um número JSON em nanossegundos.
- **Comportamento da v2**: é codificado como uma string JSON no formato `"1h2m3s"`, usando o método `time.Duration.String()`.
- **Motivo da mudança**: números em nanossegundos são pouco legíveis, e a representação textual padrão de `time.Duration` é mais útil.
- **Opções relacionadas**: é possível usar o comportamento da v1 com a opção de tag `json:",format:nano"` ou com a opção `json.FormatTimeWithLegacySemantics(true)`.

### 7. Tratamento de UTF-8 inválido

- **Comportamento da v1**: durante a serialização ou desserialização, bytes UTF-8 inválidos em strings são **substituídos silenciosamente** pelo caractere de substituição Unicode (`\uFFFD`).
- **Comportamento da v2**: por padrão, ao encontrar UTF-8 inválido, **retorna um erro**.
- **Motivo da mudança**: evitar a corrupção silenciosa de dados e seguir mais rigorosamente o padrão JSON (RFC 7493).
- **Opções relacionadas**: a opção `jsontext.AllowInvalidUTF8(true)` permite imitar o comportamento da v1.

### 8. Tratamento de nomes de membros de objeto duplicados

- **Comportamento da v1**: é permitido que um objeto JSON contenha membros com nomes duplicados. O último valor encontrado sobrescreve os anteriores.
- **Comportamento da v2**: por padrão, se houver nomes de membros duplicados, **retorna um erro**.
- **Motivo da mudança**: a RFC 8259 não define o comportamento para nomes duplicados, o que pode levar a comportamentos diferentes entre implementações. Isso pode ser uma causa de vulnerabilidades de segurança. A v2 rejeita explicitamente essa situação, aumentando a correção e a segurança.
- **Opções relacionadas**: a opção `jsontext.AllowDuplicateNames(true)` permite imitar o comportamento da v1.

---

## Diferenças de implementação e arquitetura

- **v1**: depende fortemente de `decodeState` em `decode.go` e de uma máquina de estados (state machine) escrita manualmente em `scanner.go`. Trata-se de uma estrutura monolítica, com forte acoplamento entre a lógica de análise sintática e a análise semântica.
- **v2**: a arquitetura é mais modular.
  - **`encoding/json/jsontext`**: fornece um tokenizador (`Decoder`) e um codificador (`Encoder`) JSON de baixo nível e alto desempenho. Este pacote se concentra exclusivamente nos aspectos sintáticos do JSON.
  - **`encoding/json/v2`**: com base em `jsontext`, trata a conversão semântica entre tipos Go e valores JSON.
  - Essa separação faz com que a análise sintática e a análise semântica sejam independentes, melhorando a clareza do código e o desempenho.

---

## Novas APIs e recursos da v2

A v2 oferece controle muito flexível por meio do sistema `json.Options`.

- **`json.Options`**: conjunto de opções que alteram o comportamento de serialização e desserialização.
- **`json.JoinOptions(...)`**: combina várias opções em uma única.
- **`WithMarshalers` / `WithUnmarshalers`**: recurso poderoso que permite injetar lógica de serialização e desserialização para tipos específicos sem implementar as interfaces `Marshaler` / `Unmarshaler`. É especialmente útil ao lidar com tipos de pacotes externos.
- **Novas opções**: `RejectUnknownMembers`, `Deterministic(false)`, `FormatNilSliceAsNull` e outras, que possibilitam controles de comportamento impossíveis na v1.

---

## Conclusão

O `encoding/json` v2 é uma implementação moderna que, com base na experiência da v1, melhora significativamente a **correção, o desempenho e a flexibilidade**. O comportamento padrão tornou-se mais rigoroso, mas, por meio do sofisticado sistema de `Options`, é possível reproduzir integralmente todos os comportamentos da v1, mantendo a compatibilidade com o código existente e permitindo a adoção gradual das vantagens da v2.

- Para **novos projetos**, recomenda-se usar a v2 como padrão.
- **Projetos existentes** podem continuar usando `jsonv1` ou migrar para `jsonv2`, adotando gradualmente o comportamento mais rigoroso da v2 por meio de `DefaultOptionsV1()`.
