JSON Toolkit

JSON binário: escolhendo entre MessagePack, CBOR e BSON

Resumo: MessagePack, CBOR e BSON todos comprimem JSON em bytes. Compare custo de bytes, tratamento de int64 e binário, e obtenha uma linha clara de quando vale a pena trocar.

Intermediário~10 min de leituraPublicado em 2026-10-31Atualizado em 2026-10-31
Capa da comparação entre os formatos JSON binário MessagePack, CBOR e BSON

Resumo da ópera: mantenha JSON de texto para troca externa e para dados que pessoas precisam inspecionar, e mude para MessagePack ou CBOR em links internos apenas quando payloads binários, inteiros acima de 2^53 ou decimais exatos tornarem o tamanho comprimido ou a expressividade de tipos um problema real; o BSON pertence ao ecossistema MongoDB.

O JSON de texto economiza espaços em branco; formatos binários economizam a pontuação e a forma textual dos números. Nomes de campo ainda aparecem uma vez por registro, então calcule a economia real antes de trocar.

1. O que o JSON binário realmente economiza

Defina as expectativas primeiro. Nenhum desses formatos é "outra sintaxe JSON". Eles codificam o mesmo modelo de dados em bytes: objetos, arrays, strings, números, booleanos e null. O que eles removem é concreto:

  • Caracteres estruturais como aspas, dois-pontos, vírgulas e chaves;
  • A forma textual decimal dos números, então 32 vira um único byte;
  • A reanálise para encontrar fronteiras de string, substituída por um prefixo de comprimento fixo;
  • A codificação Base64 de payloads binários, que podem ser armazenados como bytes diretamente.

O que eles não conseguem remover é igualmente concreto: nomes de campo ainda aparecem como strings em todo registro. Um map MessagePack, um map CBOR e um documento BSON todos repetem a chave para cada entrada. Então, quando seu JSON são dez mil registros com a mesma forma, a diferença de tamanho comprimido entre texto e binário é bem menor do que a intuição sugere, porque o gzip é melhor justamente em eliminar nomes de campo repetidos.

2. De onde vêm os três formatos

Formato Especificação e ano Origem Uso típico
MessagePack Especificação msgpack.org, desde 2008 Design independente focado em minimalismo Valores de cache, IPC, pipelines de log
CBOR RFC 8949 (STD 94), 2020 Influenciado pelo MessagePack, padronizado pela IETF COSE, WebAuthn, CWT, dispositivos restritos
BSON bsonspec.org, ecossistema MongoDB Projetado para documentos de banco de dados Armazenamento e protocolo de transporte do MongoDB

O CBOR é o mais padronizado: tipo de mídia application/cbor registrado na IANA, regras de codificação determinística e CDDL para descrição. O MessagePack é o mais leve, com uma especificação que cabe em uma página. O BSON é o mais pesado: ele embute ObjectId, Decimal128, datas e subtipos binários em seu sistema de tipos, o que explica por que ele só é realmente popular dentro do ecossistema MongoDB.

3. Os mesmos dados, quatro codificações

Pegue {"name":"joe"} como exemplo. Os custos de bytes são fáceis de comparar:

Codificação Bytes Sequência de bytes
Texto JSON 14 7b 22 6e 61 6d 65 22 3a 22 6a 6f 65 22 7d
MessagePack 9 81 a3 6e 61 6d 65 a3 6a 6f 65
CBOR 10 a1 64 6e 61 6d 65 63 6a 6f 65
BSON 19 Comprimento total de 4 bytes + byte de tipo + name\0 + comprimento do valor de 4 bytes + joe\0 + terminador

Separe os nove bytes do MessagePack:

  • 81: o nibble alto 1000 significa map, o nibble baixo 0001 significa um par chave-valor;
  • a3: os três bits altos 101 significam uma string de comprimento fixo 3;
  • 6e 61 6d 65: ASCII para name;
  • a3: outra string de comprimento 3;
  • 6a 6f 65: joe.

O a1 do CBOR é tipo maior 5 (map) com comprimento 1, e 64 é tipo maior 3 (string de texto) com comprimento 4, então a semântica é quase isomórfica à do MessagePack. O BSON é maior: todo documento carrega um comprimento total de 4 bytes, todo campo repete um byte de tipo e seu nome, e toda string repete um comprimento mais um \0 final. Em documentos pequenos, o BSON pode facilmente ser maior que o texto JSON.

Você pode verificar os prefixos diretamente renderizando esses bytes como hexadecimal:

const messagePackBytes = [0x81, 0xa3, 0x6e, 0x61, 0x6d, 0x65, 0xa3, 0x6a, 0x6f, 0x65];
const hexText = messagePackBytes.map((byte) => byte.toString(16)).join(' ');
console.log(hexText); // 81 a3 6e 61 6d 65 a3 6a 6f 65

3.1 Tamanho e velocidade de análise: ignore os números de marketing

As afirmações de que o binário é "10x mais rápido" devem ser divididas em duas perguntas.

Tamanho. Sem compressão, o binário é menor porque a pontuação e os espaços em branco somem. Mas pipelines reais quase sempre comprimem: após gzip ou brotli, nomes de campo repetidos são comprimidos e a vantagem do binário encolhe drasticamente. Siga o método em Minificação JSON versus gzip: meça os bytes comprimidos primeiro e depois decida se mudar de formato vale a pena.

Velocidade de análise. O binário pula a lexing (varredura de aspas, vírgulas e caracteres numéricos), então teoricamente deve ser mais rápido. Em JavaScript, no entanto, o custo dominante é alocar objetos e memória, o que nenhum decodificador consegue evitar. Forma dos dados, número de campos e qualidade da implementação importam, então a única conclusão confiável vem de medir seus próprios payloads em vez de citar o multiplicador de um fornecedor. Se um payload único for grande demais para ser lido na memória de uma vez, fatie-o com a abordagem descrita em Análise e streaming de JSON grande antes de medir o ganho de codificação.

4. int64 e campos binários: a verdadeira razão para trocar

Tamanho muitas vezes não é a razão para mudar de formato. A expressividade de tipos é.

4.1 Inteiros acima de 2^53

Números JSON caem em um double em JavaScript, então 9007199254740993 vira 9007199254740992. Os três formatos binários tratam isso de formas diferentes:

Formato Inteiros de 64 bits 64 bits sem sinal Precisão arbitrária
MessagePack ✓ int64 (0xd3), decodificável para BigInt ✓ uint64 (0xcf) ✗ (precisa de um tipo de extensão)
CBOR ✓ formas de 64 bits dos tipos maiores 0/1 ✓ Mesmo espaço de codificação ✓ tags bignum 2/3
BSON ✓ int64 (0x12) ✗ Apenas com sinal ✗ (Decimal128 é ponto fixo, não um inteiro arbitrário)

O CBOR é o único com uma representação padrão para inteiros de precisão arbitrária: valores acima de 64 bits são envolvidos na tag 2 (não negativo) ou tag 3 (negativo) em torno de uma string de bytes. MessagePack e BSON recaem em strings ou convenções de extensão próprias.

4.2 Payloads binários

O JSON tem exatamente uma forma de expressar binário: Base64, que infla o payload em cerca de um terço e exige uma cópia extra de decodificação. O MessagePack tem tipos bin (0xc4–0xc6), o CBOR tem strings de bytes (tipo maior 2) e o BSON tem subtipos binários. Quando você transporta imagens, hashes, vetores ou blocos comprimidos, essa diferença importa muito mais do que economizar alguns bytes de pontuação.

5. Comparação lado a lado

Dimensão MessagePack CBOR BSON
Especificação Especificação da comunidade RFC 8949 da IETF Ecossistema MongoDB
Tipo de mídia application/msgpack (não padrão IANA) application/cbor (registrado na IANA) Sem tipo de mídia geral
Sistema de tipos Seis tipos JSON mais bin e ext Seis tipos JSON mais bin, tags e valores simples Mais amplo: ObjectId, Decimal128, datas, regex, timestamps
Mecanismo de extensão Tipos de extensão (por exemplo timestamp -1) Tags (datas, bignums, CBOR embutido) Códigos de tipo fixos, extensões por convenção do ecossistema
Codificação determinística Sem exigência padrão Regras explícitas, utilizáveis para assinaturas Nenhuma
Autodescritivo Sim, mas sem schema em nível de campo Sim, e descritível com CDDL Sim, com nomes de campo repetidos por documento
Overhead de documento pequeno Baixo Baixo Alto (prefixo de comprimento, nomes de campo, terminador)
Ecossistema Caches, logs, algum RPC COSE, WebAuthn, IoT Apenas MongoDB

6. Suporte em navegador e Node

O Node 24 não traz nenhum desses codecs, então você instala uma biblioteca. Escolhas comuns:

Runtime MessagePack CBOR BSON
Node.js @msgpack/msgpack, msgpackr cbor-x, borc bson
Navegador @msgpack/msgpack Build de navegador do cbor-x, cbor-web bson

Ambas as famílias funcionam em Uint8Array e não exigem Buffer. Cuidado com as diferenças de versão:

import { encode, decode } from '@msgpack/msgpack';

/**
 * Codifica um objeto em bytes MessagePack e o decodifica imediatamente para verificação.
 * @param payload Objeto simples a codificar
 * @returns O valor decodificado e o comprimento de bytes codificado
 */
function roundTrip(payload: Record<string, unknown>): { decoded: unknown; byteLength: number } {
  const bytes = encode(payload);
  return { decoded: decode(bytes), byteLength: bytes.byteLength };
}

A maioria das bibliotecas decodifica inteiros grandes para number por padrão e perde precisão além da faixa segura. Se valores exatos importam, habilite a opção BigInt explicitamente (useBigInt64 em @msgpack/msgpack, um switch equivalente no cbor-x), caso contrário você simplesmente mudou o velho problema do JSON para um formato novo.

7. Depurabilidade e compatibilidade de protocolo

7.1 Depurabilidade: o maior custo oculto

A maior vantagem do JSON de texto não é o tamanho; é que você pode abri-lo a qualquer momento. Após mudar para binário:

  • grep, awk e jq todos param de funcionar, e pipelines de log precisam de uma etapa extra de decodificação;
  • Depurar produção exige o decodificador certo na versão certa, ou os nomes de campo que você vê estarão errados;
  • O tráfego capturado aparece nas ferramentas de desenvolvedor do navegador como um blob de bytes ilegível;
  • Diffs de revisão de código mostram uma sequência de hexadecimal.

O CBOR tem notação diagnóstica, que renderiza bytes de volta em texto legível próximo de JSON, por exemplo um map como {1: 2}; o MessagePack também tem alguns decodificadores de linha de comando. Mas todos exigem instalação extra e ficam fora do caminho padrão de operação.

A conclusão: sempre que pessoas precisam inspecionar os dados com frequência, o custo oculto de um formato binário supera a banda que ele economiza.

7.2 Compatibilidade de protocolo e extensões

  • CBOR tem o mecanismo de tags mais completo: tag 0 para uma string de data-hora, tag 1 para um timestamp Unix, tags 2 e 3 para bignums e tag 24 para CBOR embutido. Combinado com codificação determinística, é usado por protocolos que precisam de assinaturas e consistência entre implementações, como COSE, WebAuthn e CWT.
  • MessagePack deixa os tipos de extensão para os implementadores. Na prática, o único comum é timestamp (tipo de extensão -1). Entre equipes, todo tipo de extensão precisa estar escrito no documento de protocolo ou a outra ponta não consegue decodificar.
  • BSON tem códigos de tipo fixos, e ObjectId, Decimal128, MinKey e MaxKey são semânticas específicas do MongoDB. Tratar o BSON como formato geral de intercâmbio traz um monte de tipos não usados e prefixos de comprimento não usados.

Se um protocolo exige que "os mesmos dados produzam os mesmos bytes", só o CBOR fornece uma resposta padrão; MessagePack e BSON precisam ambos de acordo extra sobre ordem de campos e detalhes de codificação.

8. Quando vale a pena trocar

Sinais que justificam a troca:

  • Os payloads são grandes, as duas pontas podem mudar e os tamanhos comprimidos ainda diferem de forma significativa;
  • Você transporta conteúdo binário e a inflação do Base64 e o custo de decodificação são mensuráveis;
  • Você precisa de inteiros de 64 bits ou decimais exatos que os doubles JSON não conseguem representar;
  • O link é servidor a servidor e a ferramenta de diagnóstico pode ser padronizada.

Sinais que argumentam contra:

  • O gzip ou brotli já está em vigor e o ganho é de um dígito percentual;
  • A API é pública e os terceiros só têm clientes HTTP generalistas;
  • Os dados são logs ou configuração que pessoas leem à mão;
  • A equipe não tem orçamento para manter decodificadores e compatibilidade de versão.

Um caminho intermediário também funciona: mantenha JSON externamente e use MessagePack para caches internos. Valores de cache são lidos apenas pelos seus próprios serviços, então tanto tamanho quanto custo de análise melhoram, e limpar o cache resolve qualquer problema sem tocar no contrato.

9. Lista de verificação

  • [ ] Compare contagens de bytes comprimidos de dados reais, não contagens sem compressão;
  • [ ] Decida a política de inteiros grandes: opção BigInt, transporte por string ou bignums CBOR;
  • [ ] Envie campos binários como bin ou strings de bytes em vez de envolvê-los em Base64;
  • [ ] Escreva tipos de extensão e tags no documento de protocolo e fixe as versões dos decodificadores;
  • [ ] Mantenha um caminho de depuração em JSON de texto que possa ser habilitado durante incidentes de produção;
  • [ ] Rode um benchmark e registre números reproduzíveis em vez de alegações verbais.

Qual é a diferença entre MessagePack e CBOR?

As codificações são muito parecidas: maps, arrays e strings usam todos um prefixo compacto de tipo mais comprimento. A principal diferença é a padronização. O CBOR é uma RFC da IETF com tipo de mídia na IANA, mecanismo de tags e regras de codificação determinística; o MessagePack é uma especificação da comunidade cujos tipos de extensão dependem de acordos privados. Escolha CBOR quando precisar de assinaturas, consistência entre implementações ou protocolos para dispositivos restritos.

BSON pode substituir o JSON como formato geral?

Não é recomendado. O BSON foi projetado para documentos de banco de dados: todo documento carrega um comprimento total de 4 bytes, todo campo repete seu tipo e nome, e documentos pequenos acabam maiores que o texto JSON. Ele também inclui tipos específicos do MongoDB como ObjectId e MinKey. Fora do MongoDB, a maior parte do seu sistema de tipos fica sem uso enquanto a penalidade de tamanho permanece.

Quanto menor um formato binário torna meu payload?

Depende da forma dos dados. Quando nomes de campo se repetem muito, JSON de texto comprimido e formatos binários ficam próximos; quando os nomes são curtos e os dados estão cheios de caracteres estruturais e números, a vantagem binária é mais clara. Meça amostras reais sem compressão e após gzip, e depois decida, em vez de estimar com um multiplicador genérico.

Ainda consigo depurar dados binários com jq?

Não. jq só processa JSON de texto e vai falhar imediatamente em bytes MessagePack ou CBOR. Você precisa decodificar com a biblioteca correspondente para JSON e então passar para o jq; o CBOR também pode passar por uma ferramenta de notação diagnóstica para virar texto legível. Manter um caminho de depuração em texto é mais barato do que instalar ferramentas sob demanda.