O formato JSON nativamente não suporta dados binários. Os dados binários precisam ser escapados para que possam ser colocados em um elemento de sequência (ou seja, zero ou mais caracteres Unicode entre aspas duplas usando escapes de barra invertida) no JSON.
Um método óbvio para escapar de dados binários é usar o Base64. No entanto, o Base64 possui uma alta sobrecarga de processamento. Também expande 3 bytes em 4 caracteres, o que aumenta o tamanho dos dados em cerca de 33%.
Um caso de uso para isso é o rascunho v0.8 da especificação da API de armazenamento em nuvem CDMI . Você cria objetos de dados por meio de um serviço da Web REST usando JSON, por exemplo
PUT /MyContainer/BinaryObject HTTP/1.1
Host: cloud.example.com
Accept: application/vnd.org.snia.cdmi.dataobject+json
Content-Type: application/vnd.org.snia.cdmi.dataobject+json
X-CDMI-Specification-Version: 1.0
{
"mimetype" : "application/octet-stream",
"metadata" : [ ],
"value" : "TWFuIGlzIGRpc3Rpbmd1aXNoZWQsIG5vdCBvbmx5IGJ5IGhpcyByZWFzb24sIGJ1dCBieSB0aGlz
IHNpbmd1bGFyIHBhc3Npb24gZnJvbSBvdGhlciBhbmltYWxzLCB3aGljaCBpcyBhIGx1c3Qgb2Yg
dGhlIG1pbmQsIHRoYXQgYnkgYSBwZXJzZXZlcmFuY2Ugb2YgZGVsaWdodCBpbiB0aGUgY29udGlu
dWVkIGFuZCBpbmRlZmF0aWdhYmxlIGdlbmVyYXRpb24gb2Yga25vd2xlZGdlLCBleGNlZWRzIHRo
ZSBzaG9ydCB2ZWhlbWVuY2Ugb2YgYW55IGNhcm5hbCBwbGVhc3VyZS4=",
}
Existem maneiras melhores e métodos padrão para codificar dados binários em strings JSON?
JSON.parse
etc ......Respostas:
Existem 94 caracteres Unicode que podem ser representados como um byte de acordo com a especificação JSON (se o seu JSON for transmitido como UTF-8). Com isso em mente, acho que o melhor que você pode fazer em termos de espaço é a base85, que representa quatro bytes como cinco caracteres. No entanto, essa é apenas uma melhoria de 7% em relação à base64, é mais caro computar e as implementações são menos comuns do que na base64, portanto, provavelmente não é uma vitória.
Você também pode simplesmente mapear cada byte de entrada para o caractere correspondente em U + 0000-U + 00FF e, em seguida, executar a codificação mínima exigida pelo padrão JSON para transmitir esses caracteres; a vantagem aqui é que a decodificação necessária é nula além das funções internas, mas a eficiência de espaço é ruim - uma expansão de 105% (se todos os bytes de entrada forem igualmente prováveis) vs. 25% para base85 ou 33% para base64.
Veredicto final: base64 vence, na minha opinião, pelo fato de ser comum, fácil e não ruim o suficiente para justificar a substituição.
Veja também: Base91 e Base122
fonte
base64.b85encode()
eb85decode()
agora. Uma medição simples do tempo de codificação + decodificação mostra que b85 é mais de 13 vezes mais lento que b64. Portanto, temos 7% de vitória no tamanho, mas 1300% de perda de desempenho.DEL
como um caractere de controle.Encontrei o mesmo problema e pensei em compartilhar uma solução: multipart / form-data.
Ao enviar um formulário com várias partes, você envia primeiro como string seus metadados JSON e, em seguida, envia separadamente como binário bruto (imagem (s), wavs, etc) indexado pelo nome Disposição de Conteúdo .
Aqui está um bom tutorial sobre como fazer isso no obj-c, e aqui está um artigo de blog que explica como particionar os dados da string com o limite do formulário e separá-lo dos dados binários.
A única alteração que você realmente precisa fazer é no lado do servidor; você precisará capturar seus metadados que devem fazer referência aos dados binários do POST adequadamente (usando um limite de Disposição de conteúdo).
Concedido, requer um trabalho adicional no lado do servidor, mas se você estiver enviando muitas imagens ou imagens grandes, vale a pena. Combine isso com a compactação gzip, se desejar.
O IMHO que envia dados codificados em base64 é um hack; o RFC multipart / form-data foi criado para problemas como este: envio de dados binários em combinação com texto ou metadados.
fonte
O problema com o UTF-8 é que não é a codificação com mais espaço eficiente. Além disso, algumas seqüências aleatórias de bytes binários são codificação UTF-8 inválida. Portanto, você não pode simplesmente interpretar uma sequência de bytes binários aleatória como alguns dados UTF-8, porque será uma codificação UTF-8 inválida. O benefício dessa restrição na codificação UTF-8 é que ela torna robusta e possível localizar caracteres de vários bytes iniciando e terminando qualquer byte que começamos a observar.
Como conseqüência, se a codificação de um valor de byte no intervalo [0..127] precisaria de apenas um byte na codificação UTF-8, a codificação de um valor de byte no intervalo [128..255] exigiria 2 bytes! Pior que isso. No JSON, chars de controle "e \ não têm permissão para aparecer em uma sequência. Portanto, os dados binários exigiriam que alguma transformação fosse codificada corretamente.
Vamos ver. Se assumirmos valores de bytes aleatórios distribuídos uniformemente em nossos dados binários, então, em média, metade dos bytes será codificada em um bytes e a outra metade em dois bytes. Os dados binários codificados em UTF-8 teriam 150% do tamanho inicial.
A codificação Base64 cresce apenas para 133% do tamanho inicial. Portanto, a codificação Base64 é mais eficiente.
Que tal usar outra codificação Base? No UTF-8, a codificação dos 128 valores ASCII é a mais eficiente em espaço. Em 8 bits, você pode armazenar 7 bits. Portanto, se cortarmos os dados binários em pedaços de 7 bits para armazená-los em cada byte de uma string codificada em UTF-8, os dados codificados crescerão apenas para 114% do tamanho inicial. Melhor que o Base64. Infelizmente, não podemos usar esse truque fácil, porque o JSON não permite alguns caracteres ASCII. Os 33 caracteres de controle ASCII ([0..31] e 127) e o "e \ devem ser excluídos. Isso nos deixa apenas 128-35 = 93 caracteres.
Portanto, em teoria, poderíamos definir uma codificação Base93 que aumentaria o tamanho codificado para 8 / log2 (93) = 8 * log10 (2) / log10 (93) = 122%. Mas uma codificação Base93 não seria tão conveniente quanto uma codificação Base64. O Base64 requer o corte da sequência de bytes de entrada em blocos de 6 bits, para os quais a operação bit a bit simples funciona bem. Além de 133%, não há muito mais que 122%.
É por isso que cheguei independentemente à conclusão comum de que o Base64 é realmente a melhor opção para codificar dados binários no JSON. Minha resposta apresenta uma justificativa para isso. Concordo que não é muito atraente do ponto de vista do desempenho, mas considere também o benefício de usar JSON com sua representação de string legível por humanos fácil de manipular em todas as linguagens de programação.
Se o desempenho for crítico, uma codificação binária pura deve ser considerada como substituição do JSON. Mas com o JSON, minha conclusão é que o Base64 é o melhor.
fonte
BSON (JSON binário) pode funcionar para você. http://en.wikipedia.org/wiki/BSON
Edit: FYI, a biblioteca .NET json.net suporta leitura e gravação de bson se você estiver procurando por algum amor em C # pelo lado do servidor.
fonte
Se você lidar com problemas de largura de banda, tente compactar os dados primeiro no lado do cliente e depois base64-it.
Um bom exemplo dessa mágica está em http://jszip.stuartk.co.uk/ e mais discussão sobre esse tópico está na implementação do Gzip em JavaScript
fonte
Content-Encoding
), pois o base64 compacta muito bem.O yEnc pode funcionar para você:
http://en.wikipedia.org/wiki/Yenc
No entanto, o yEnc é uma codificação de 8 bits, portanto, armazená-lo em uma string JSON tem os mesmos problemas que armazenar os dados binários originais - fazê-lo da maneira ingênua significa cerca de uma expansão de 100%, o que é pior que o base64.
fonte
Embora seja verdade que a base64 tenha ~ 33% de taxa de expansão, não é necessariamente verdade que a sobrecarga de processamento seja significativamente mais do que isso: depende realmente da biblioteca / kit de ferramentas JSON que você está usando. Codificação e decodificação são operações simples e diretas, e podem até ser otimizadas para codificação de caracteres wrt (como o JSON suporta apenas UTF-8/16/32) - os caracteres base64 são sempre de byte único para as entradas da string JSON. Por exemplo, na plataforma Java, existem bibliotecas que podem fazer o trabalho com bastante eficiência, de modo que a sobrecarga se deve principalmente ao tamanho expandido.
Eu concordo com duas respostas anteriores:
fonte
Formato de sorriso
É muito rápido para codificar, decodificar e compactar
Comparação de velocidade (baseada em java, mas significativa): https://github.com/eishay/jvm-serializers/wiki/
Também é uma extensão para JSON que permite ignorar a codificação base64 para matrizes de bytes
Seqüências de caracteres codificadas por sorriso podem ser compactadas quando o espaço é crítico
fonte
( Edite 7 anos depois: o Google Gears se foi. Ignore esta resposta.)
A equipe do Google Gears encontrou o problema da falta de tipos de dados binários e tentou resolvê-lo:
Talvez você possa tecer isso de alguma forma.
fonte
Como você procura a capacidade de converter dados binários em um formato estritamente baseado em texto e muito limitado, acho que a sobrecarga do Base64 é mínima em comparação com a conveniência que você espera manter com o JSON. Se a capacidade de processamento e a taxa de transferência forem uma preocupação, você provavelmente precisará reconsiderar seus formatos de arquivo.
fonte
Apenas para adicionar o ponto de vista de recursos e complexidade à discussão. Como você faz PUT / POST e PATCH para armazenar novos recursos e alterá-los, deve-se lembrar que a transferência de conteúdo é uma representação exata do conteúdo que é armazenado e recebido pela emissão de uma operação GET.
Uma mensagem de várias partes é frequentemente usada como salvadora, mas por motivos de simplicidade e para tarefas mais complexas, prefiro a ideia de fornecer o conteúdo como um todo. É auto-explicativo e é simples.
E sim, o JSON é algo incapacitante, mas no final o próprio JSON é detalhado. E a sobrecarga do mapeamento para o BASE64 é uma maneira pequena.
Usando as mensagens de várias partes corretamente, é necessário desmontar o objeto a ser enviado, usar um caminho de propriedade como o nome do parâmetro para a combinação automática ou será necessário criar outro protocolo / formato para expressar apenas a carga útil.
Também gostando da abordagem BSON, isso não é tão amplo e facilmente suportado como se gostaria.
Basicamente, apenas faltamos alguma coisa aqui, mas a incorporação de dados binários como base64 está bem estabelecida e é um caminho a menos, a menos que você realmente tenha identificado a necessidade de fazer a transferência binária real (o que geralmente não é o caso).
fonte
Eu cavo um pouco mais (durante a implementação da base128 ) e exponho que, quando enviamos caracteres cujos códigos ascii são maiores que 128, o navegador (chrome) na verdade envia dois caracteres (bytes) em vez de um :( . O motivo é que o JSON por padrão, use utf8 caracteres para os quais os caracteres com códigos ASCII acima de 127 são codificados por dois bytes, o que foi mencionado pela resposta chmike.Fiz teste desta maneira: digite chrome bar na barra de URL chrome: // net-export / , selecione "Incluir bruto bytes ", comece a capturar, envie solicitações POST (usando o snippet na parte inferior), pare de capturar e salve o arquivo json com dados brutos das solicitações. Depois, examinamos o arquivo json:
4142434445464748494a4b4c4d4e
esta é a codificação hexadecimalABCDEFGHIJKLMN
e veremos isso"byte_count": 639
nela.C2BCC2BDC380C381C382C383C384C385C386C387C388C389C38AC38B
this is request -hex utf8 códigos de caracteres¼½ÀÁÂÃÄÅÆÇÈÉÊË
(no entanto, os códigos hexadecimais ASCII desses caracteres sãoc1c2c3c4c5c6c7c8c9cacbcccdce
). Por"byte_count": 703
isso, são 64 bytes a mais do que a solicitação base64 porque os caracteres com códigos ascii acima de 127 têm código de 2 bytes na solicitação :(Portanto, na verdade, não temos lucro com o envio de caracteres com códigos> 127 :(. Para cadeias base64, não observamos um comportamento tão negativo (provavelmente também para base85 - eu não a verifico) - no entanto, pode haver alguma solução para esse problema. envio de dados na parte binária do POST multipart / form-data descrito na resposta Ælex (no entanto, geralmente nesse caso, não precisamos usar nenhuma codificação básica ...).
A abordagem alternativa pode depender do mapeamento de uma porção de dados de dois bytes em um caractere utf8 válido, codificando-o usando algo como base65280 / base65k, mas provavelmente seria menos eficaz que base64 devido à especificação utf8 ...
Mostrar snippet de código
fonte
O tipo de dados realmente preocupa. Testei diferentes cenários ao enviar a carga útil de um recurso RESTful. Para codificação, usei o Base64 (Apache) e o GZIP para compactação (java.utils.zip. *). A carga contém informações sobre filme, imagem e arquivo de áudio. Compactei e codifiquei os arquivos de imagem e áudio que degradaram drasticamente o desempenho. A codificação antes da compactação acabou bem. O conteúdo da imagem e do áudio foi enviado como bytes codificado e compactado [].
fonte
Consulte: http://snia.org/sites/default/files/Multi-part%20MIME%20Extension%20v1.0g.pdf
Ele descreve uma maneira de transferir dados binários entre um cliente e servidor CDMI usando operações 'tipo de conteúdo CDMI', sem exigir a conversão base64 dos dados binários.
Se você pode usar a operação 'Tipo de conteúdo não CDMI', é ideal transferir 'dados' para / de um objeto. Mais tarde, os metadados podem ser adicionados / recuperados no / para o objeto como uma operação subsequente 'tipo de conteúdo CDMI'.
fonte
Minha solução agora, XHR2 está usando ArrayBuffer. O ArrayBuffer como sequência binária contém conteúdo de várias partes, vídeo, áudio, gráfico, texto e assim por diante com vários tipos de conteúdo. Tudo em uma resposta.
No navegador moderno, possui DataView, StringView e Blob para diferentes componentes. Veja também: http://rolfrost.de/video.html para mais detalhes.
fonte
[16, 2, 38, 89]
que é muito ineficiente.