← Voltar para o blog

**Go JSON Evolui: Uma Nova API Experimental para o Futuro**

Resumo em português do artigo oficial do Go Blog: **Go JSON Evolui: Uma Nova API Experimental para o Futuro**

O artigo do blog oficial do Go anuncia o lançamento de uma nova API experimental para JSON, presente nos pacotes encoding/json/v2 e encoding/json/jsontext, com o objetivo de abordar as limitações e problemas existentes no pacote encoding/json (denominado v1 neste contexto). Após quase 15 anos desde a introdução do suporte a JSON em Go, e com o JSON se tornando o formato de dados mais popular na internet, a equipe de desenvolvimento do Go reconheceu a necessidade de evoluir o pacote padrão para atender às demandas modernas e corrigir falhas identificadas ao longo do tempo.

Problemas Identificados no encoding/json (v1)

O artigo detalha uma série de problemas com o pacote encoding/json original, categorizados em falhas de comportamento, deficiências na API e limitações de desempenho.

Falhas de Comportamento

  • Manipulação Imprecisa da Sintaxe JSON: O encoding/json é permissivo com relação a sintaxes JSON inválidas.
    • Aceita UTF-8 inválido, enquanto o padrão mais recente (RFC 8259) exige UTF-8 válido. Isso pode levar a corrupção silenciosa de dados.
    • Aceita objetos com nomes de membros duplicados, o que pode levar a comportamentos inesperados e até mesmo vulnerabilidades de segurança. A RFC 8259 não especifica como lidar com nomes duplicados, deixando a implementação a cargo do interpretador JSON. Isso pode resultar em diferentes interpretações do mesmo JSON por diferentes sistemas, um risco explorado em ataques como o CVE-2017-12635.
  • Vazamento da “nulidade” de slices e maps: encoding/json serializa slices e maps nulos como null em JSON. Isso pode causar problemas de interoperabilidade com sistemas que não aceitam null para arrays ou objetos. A maioria dos usuários Go prefere que slices e maps nulos sejam serializados como arrays ou objetos vazios por padrão.
  • Unmarshaling Insensível ao Caso: A correspondência entre nomes de membros JSON e campos de structs Go é feita de forma case-insensitive. Isso é um comportamento surpreendente, pode ser uma vulnerabilidade de segurança e limita o desempenho.
  • Chamadas Inconsistentes de Métodos: Métodos MarshalJSON definidos em um receptor ponteiro são chamados de forma inconsistente pelo encoding/json devido a detalhes de implementação. Embora seja considerado um bug, não pode ser corrigido devido à dependência de muitas aplicações no comportamento atual.

Deficiências na API

  • Dificuldade em Unmarshaling de um io.Reader: É difícil garantir que não haja “lixo” (trailing data) após um valor JSON válido ao deserializar de um io.Reader usando json.NewDecoder(r).Decode(v).
  • Opções Limitadas: As opções definidas nos tipos Encoder e Decoder não podem ser usadas com as funções Marshal e Unmarshal. Da mesma forma, tipos que implementam as interfaces Marshaler e Unmarshaler não podem usar as opções, criando uma inconsistência na aplicação de configurações. Por exemplo, a opção Decoder.DisallowUnknownFields perde o efeito ao chamar um método UnmarshalJSON personalizado.
  • Funções de Formatação Limitadas: As funções Compact, Indent e HTMLEscape gravam em um bytes.Buffer em vez de uma interface mais flexível como []byte ou io.Writer, o que limita sua usabilidade.

Limitações de Desempenho

  • MarshalJSON: A interface MarshalJSON força a alocação de um []byte retornado. Além disso, o encoding/json deve verificar se o resultado é JSON válido e reformatá-lo para corresponder à indentação especificada.
  • UnmarshalJSON: A interface UnmarshalJSON requer um valor JSON completo (sem dados extras). Isso força o encoding/json a analisar o valor JSON a ser deserializado por completo para determinar onde termina antes de chamar UnmarshalJSON. Depois, o método UnmarshalJSON em si deve analisar o valor JSON fornecido novamente.
  • Falta de Streaming: Embora os tipos Encoder e Decoder operem em um io.Writer ou io.Reader, eles armazenam em buffer o valor JSON inteiro na memória. O método Decoder.Token para ler tokens individuais é pesado em alocação, e não há uma API correspondente para escrever tokens.

Além disso, se a implementação de um método MarshalJSON ou UnmarshalJSON chama recursivamente a função Marshal ou Unmarshal, o desempenho se torna quadrático.

A Necessidade de uma Nova Versão (v2)

O artigo explica que, embora a equipe de desenvolvimento tenha considerado corrigir o pacote encoding/json diretamente, as limitações da API existente e a promessa de compatibilidade do Go 1 tornaram isso impraticável. A introdução de uma nova versão principal (v2) permite quebrar a compatibilidade com a versão anterior e introduzir as mudanças necessárias para resolver os problemas identificados.

A alternativa de criar funções separadas, como MarshalV2 ou UnmarshalV2, foi considerada, mas descartada por ser equivalente a criar um namespace paralelo dentro do mesmo pacote. A solução encontrada foi criar o pacote encoding/json/v2, que oferece um novo namespace onde as mudanças podem ser feitas sem afetar a compatibilidade com o encoding/json original (v1).

Planejamento do encoding/json/v2

O planejamento para a nova versão do encoding/json levou anos. Em 2020, a impossibilidade de corrigir os problemas no pacote existente impulsionou Daniel Martí a elaborar um documento sobre como seria um pacote v2 hipotético. Joe Tsai, após trabalhar na API do Go para Protocol Buffers, percebeu que o pacote protojson precisava usar uma implementação JSON personalizada, pois o encoding/json não era capaz de aderir ao padrão JSON mais rigoroso exigido pela especificação do Protocol Buffer, nem de serializar JSON de forma eficiente em streaming.

Daniel e Joe uniram forças para criar um protótipo para o v2, com o código inicial sendo uma versão aprimorada da lógica de serialização JSON do módulo Go protobuf. Ao longo do tempo, outros (Roger Peppe, Chris Hines, Johan Brandhorst-Satzkorn e Damien Neil) se juntaram ao esforço, fornecendo revisão de design, revisão de código e testes de regressão.

O Novo Pacote encoding/json/v2

O artigo não entra em detalhes específicos sobre a nova API, mas implicitamente sugere que ela abordará os problemas mencionados acima:

  • Sintaxe JSON Mais Estrita: A nova API deve ser mais rigorosa na validação da sintaxe JSON, rejeitando UTF-8 inválido e objetos com nomes de membros duplicados.
  • Controle da “Nulidade”: A nova API provavelmente oferecerá opções para controlar como slices e maps nulos são serializados (como null ou como arrays/objetos vazios).
  • Unmarshaling Sensível ao Caso: A nova API provavelmente usará case-sensitive por padrão.
  • API Mais Consistente: A nova API deve ter uma API mais consistente, com opções que podem ser usadas com todas as funções de marshaling e unmarshaling, e a capacidade de passar opções para métodos MarshalJSON e UnmarshalJSON personalizados.
  • Melhor Desempenho: A nova API pode oferecer suporte a streaming para reduzir o uso de memória e evitar a necessidade de analisar o JSON várias vezes.

O Pacote encoding/jsontext

O artigo também menciona o pacote encoding/jsontext, que provavelmente lida com a representação textual de JSON e pode oferecer funcionalidades adicionais relacionadas à manipulação de texto JSON. Detalhes específicos sobre este pacote não são fornecidos no texto.

Status Experimental e Próximos Passos

É crucial notar que os pacotes encoding/json/v2 e encoding/jsontext são experimentais e podem sofrer alterações na API no futuro. O objetivo é coletar feedback da comunidade e refinar a API antes de torná-la estável.

Para usar os pacotes experimentais, é necessário importá-los explicitamente:

import "encoding/json/v2"
import "encoding/json/jsontext"

A equipe de desenvolvimento incentiva os usuários a experimentar a nova API e fornecer feedback para ajudar a moldar o futuro do JSON em Go.

Em resumo, o lançamento dos pacotes encoding/json/v2 e encoding/jsontext representa um passo importante na evolução do suporte a JSON em Go, visando resolver problemas de longa data e fornecer uma API mais moderna, flexível e eficiente. A fase experimental é fundamental para garantir que a nova API atenda às necessidades da comunidade Go e se torne um padrão robusto para o futuro.


Artigo Original

Este e um resumo em português do artigo original publicado no blog oficial do Go.

Titulo original: A new experimental Go API for JSON

Leia o artigo completo em ingles no Go Blog

Autor original: Joe Tsai, Daniel Martí, Johan Brandhorst-Satzkorn, Roger Peppe, Chris Hines, and Damien Neil