Domanda

Il formato JSON non supporta nativamente i dati binari. I dati binari devono essere sottoposti a escape in modo che possano essere inseriti in un elemento stringa (ovvero zero o più caratteri Unicode tra virgolette doppie utilizzando escape barra rovesciata) in JSON.

Un metodo ovvio per sfuggire ai dati binari è usare Base64. Tuttavia, Base64 ha un elevato sovraccarico di elaborazione. Inoltre, espande 3 byte in 4 caratteri, il che comporta un aumento delle dimensioni dei dati di circa il 33%.

Un caso d'uso per questo è la bozza v0.8 della Specifica API di archiviazione cloud CDMI . Si creano oggetti dati tramite un servizio Web REST utilizzando JSON, ad es.

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=",
}

Esistono modi migliori e metodi standard per codificare i dati binari nelle stringhe JSON?

È stato utile?

Soluzione

Esistono 94 caratteri Unicode che possono essere rappresentati come un byte in base alle specifiche JSON (se il JSON viene trasmesso come UTF-8). Con questo in mente, penso che il meglio che puoi fare nello spazio sia base85 che rappresenta quattro byte come cinque personaggi. Tuttavia, questo è solo un miglioramento del 7% rispetto a base64, è più costoso da calcolare e le implementazioni sono meno comuni rispetto a base64, quindi probabilmente non è una vittoria.

Potresti anche semplicemente mappare ogni byte di input sul carattere corrispondente in U + 0000-U + 00FF, quindi eseguire la codifica minima richiesta dallo standard JSON per passare quei caratteri; il vantaggio qui è che la decodifica richiesta è nulla oltre le funzioni integrate, ma l'efficienza dello spazio è scarsa - un'espansione del 105% (se tutti i byte di input sono ugualmente probabili) rispetto al 25% per base85 o 33% per base64.

Verdetto finale: base64 vince, secondo me, sulla base del fatto che è comune, facile e non male da giustificare la sostituzione.

Vedi anche: Base91

Altri suggerimenti

Ho riscontrato lo stesso problema e ho pensato di condividere una soluzione: multipart/form-data.

Inviando un modulo multipart, invii prima come stringa i tuoi metadati JSON , quindi invii separatamente come binari non elaborati (immagini, onde, ecc.) indicizzati dal Contenuto -Disposizione nome

Ecco un bel tutorial su come farlo in obj-c, ed ecco un articolo del blog spiega come partizionare i dati di stringa con il limite del modulo e separarli dai dati binari.

L'unica modifica che devi veramente fare è sul lato server; dovrai acquisire i tuoi metadati che dovrebbero fare riferimento in modo appropriato ai dati binari POST (usando un confine di disposizione dei contenuti).

Concesso, richiede ulteriore lavoro sul lato server, ma se stai inviando molte immagini o immagini di grandi dimensioni, ne vale la pena. Combina questo con la compressione gzip se vuoi.

L'IMHO che invia dati con codifica base64 è un hack; i dati multipart / form RFC sono stati creati per problemi come questo: invio di dati binari in combinazione con testo o metadati.

BSON (Binary JSON) potrebbe funzionare per te. http://en.wikipedia.org/wiki/BSON

Modifica: Cordiali saluti, la libreria .NET json.net supporta la lettura e la scrittura di bson se stai cercando un po 'di amore lato server C #.

Il problema con UTF-8 è che non è la codifica più efficiente in termini di spazio. Inoltre, alcune sequenze casuali di byte binari sono codifiche UTF-8 non valide. Quindi non puoi semplicemente interpretare una sequenza di byte binari casuali come alcuni dati UTF-8 perché sarà una codifica UTF-8 non valida. Il vantaggio di questo vincolo sulla codifica UTF-8 è che rende robusto e possibile individuare i caratteri multibyte che iniziano e finiscono qualunque byte iniziamo a guardare.

Di conseguenza, se la codifica di un valore di byte nell'intervallo [0..127] richiederebbe solo un byte nella codifica UTF-8, la codifica di un valore di byte nell'intervallo [128..255] richiederebbe 2 byte! Peggio ancora. In JSON, controlla i caratteri, " e \ non possono apparire in una stringa. Quindi i dati binari richiederebbero una certa trasformazione per essere codificati correttamente.

Vediamo. Se assumiamo valori di byte casuali distribuiti uniformemente nei nostri dati binari, in media, la metà dei byte verrebbe codificata in un byte e l'altra metà in due byte. I dati binari codificati UTF-8 avrebbero il 150% della dimensione iniziale.

La codifica Base64 aumenta solo al 133% della dimensione iniziale. Quindi la codifica Base64 è più efficiente.

Che dire dell'utilizzo di un'altra codifica Base? In UTF-8, la codifica dei 128 valori ASCII è la più efficiente in termini di spazio. In 8 bit è possibile memorizzare 7 bit. Quindi se tagliamo i dati binari in blocchi di 7 bit per memorizzarli in ogni byte di una stringa codificata UTF-8, i dati codificati aumenterebbero solo del 114% della dimensione iniziale. Meglio di Base64. Sfortunatamente non possiamo usare questo semplice trucco perché JSON non consente alcuni caratteri ASCII. I 33 caratteri di controllo di ASCII ([0..31] e 127) e il " e \ deve essere escluso. Questo ci lascia solo 128-35 = 93 caratteri.

Quindi in teoria potremmo definire una codifica Base93 che aumenterebbe la dimensione codificata a 8 / log2 (93) = 8 * log10 (2) / log10 (93) = 122%. Ma una codifica Base93 non sarebbe conveniente come una codifica Base64. Base64 richiede di tagliare la sequenza di byte di input in blocchi di 6 bit per i quali la semplice operazione bit a bit funziona bene. Oltre il 133% non è molto più del 122%.

Questo è il motivo per cui sono giunto indipendentemente alla conclusione comune che Base64 è davvero la scelta migliore per codificare i dati binari in JSON. La mia risposta presenta una giustificazione per questo. Sono d'accordo che non è molto attraente dal punto di vista delle prestazioni, ma considero anche il vantaggio di usare JSON con la sua rappresentazione di stringhe leggibili dall'uomo facile da manipolare in tutti i linguaggi di programmazione.

Se le prestazioni sono fondamentali rispetto a una codifica binaria pura, devono essere considerate come sostituzione di JSON. Ma con JSON la mia conclusione è che Base64 è il migliore.

Se hai a che fare con problemi di larghezza di banda, prova prima a comprimere i dati sul lato client, poi base64-it.

Un bell'esempio di tale magia è disponibile all'indirizzo http://jszip.stuartk.co.uk/ e ulteriori discussioni su questo argomento sono disponibili all'indirizzo implementazione JavaScript di Gzip

yEnc potrebbe funzionare per te:

http://en.wikipedia.org/wiki/Yenc

  

" yEnc è uno schema di codifica da binario a testo per il trasferimento binario   file in [testo]. Riduce il sovraccarico rispetto ai precedenti basati su US-ASCII   metodi di codifica utilizzando un metodo di codifica ASCII estesa a 8 bit.   L'overhead di yEnc è spesso (se ogni valore di byte appare approssimativamente   con la stessa frequenza in media) appena dell'1–2%, rispetto a   33% –40% di overhead per metodi di codifica a 6 bit come uuencode e Base64.   ... Nel 2003 yEnc è diventato di fatto il sistema di codifica standard per   file binari su Usenet. "

Tuttavia, yEnc è una codifica a 8 bit, quindi memorizzarlo in una stringa JSON ha gli stessi problemi della memorizzazione dei dati binari originali - farlo in modo ingenuo significa circa un'espansione del 100%, che è peggio di base64.

Formato sorriso

È molto veloce codificare, decodificare e compattare

Confronto della velocità (basato su java ma comunque significativo): https://github.com/eishay/jvm-serializers/ wiki /

Inoltre è un'estensione di JSON che consente di saltare la codifica Base64 per array di byte

Le stringhe codificate con sorriso possono essere compresse con gzip quando lo spazio è critico

Sebbene sia vero che base64 abbia una velocità di espansione del 33%, non è necessariamente vero che il sovraccarico di elaborazione sia significativamente superiore a questo: dipende in realtà dalla libreria / toolkit JSON che si sta utilizzando. La codifica e la decodifica sono semplici operazioni dirette e possono anche essere ottimizzate per la codifica dei caratteri wrt (poiché JSON supporta solo UTF-8/16/32) - i caratteri base64 sono sempre a byte singolo per le voci String JSON. Ad esempio sulla piattaforma Java ci sono librerie che possono fare il lavoro in modo piuttosto efficiente, quindi il sovraccarico è dovuto principalmente alle dimensioni espanse.

Sono d'accordo con due risposte precedenti:

  • base64 è uno standard semplice e comunemente usato, quindi è improbabile trovare qualcosa di meglio da usare specificamente con JSON (base-85 è usato da Postscript ecc; ma i vantaggi sono nella migliore delle ipotesi marginali se ci pensate)
  • la compressione prima della codifica (e dopo la decodifica) può avere molto senso, a seconda dei dati che usi

( Modifica 7 anni dopo: Google Gears non c'è più. Ignora questa risposta.)


Il team di Google Gears ha riscontrato il problema della mancanza di dati binari e ha tentato di risolverlo:

  

API BLOB

     

JavaScript ha un tipo di dati incorporato per le stringhe di testo, ma nulla per i dati binari. L'oggetto BLOB tenta di risolvere questa limitazione.

Forse puoi intrecciarlo in qualche modo.

Poiché stai cercando la possibilità di inserire i dati binari in un formato strettamente basato su testo e molto limitato, penso che l'overhead di Base64 sia minimo rispetto alla comodità che ti aspetti di mantenere con JSON. Se la potenza di elaborazione e la velocità effettiva sono un problema, probabilmente dovrai riconsiderare i tuoi formati di file.

Solo per aggiungere il punto di vista delle risorse e della complessità alla discussione. Dal momento che esegui PUT / POST e PATCH per archiviare nuove risorse e modificarle, si dovrebbe ricordare che il trasferimento del contenuto è una rappresentazione esatta del contenuto archiviato e che viene ricevuto eseguendo un'operazione GET.

Un messaggio in più parti è spesso usato come un salvatore, ma per ragioni di semplicità e per compiti più complessi, preferisco l'idea di dare il contenuto nel suo insieme. Si spiega da sé ed è semplice.

E sì, JSON è qualcosa di paralizzante, ma alla fine JSON stesso è prolisso. E il sovraccarico della mappatura su BASE64 è un modo per ridimensionare.

Utilizzando correttamente i messaggi multiparte si deve smantellare l'oggetto da inviare, utilizzare un percorso di proprietà come nome del parametro per la combinazione automatica o sarà necessario creare un altro protocollo / formato per esprimere semplicemente il payload.

Apprezzando anche l'approccio BSON, questo non è così ampiamente e facilmente supportato come si vorrebbe che fosse.

Fondamentalmente, ci manca qualcosa qui, ma incorporare i dati binari come base64 è ben definito e ben fatto a meno che tu non abbia davvero identificato la necessità di effettuare il vero trasferimento binario (cosa che spesso non accade).

Il tipo di dati riguarda davvero. Ho testato diversi scenari sull'invio del payload da una risorsa RESTful. Per la codifica ho usato Base64 (Apache) e per la compressione GZIP (java.utils.zip. *). Il payload contiene informazioni su film, un'immagine e un file audio. Ho compresso e codificato i file di immagine e audio che hanno degradato drasticamente le prestazioni. La codifica prima della compressione è risultata buona. Il contenuto di immagini e audio è stato inviato come byte codificati e compressi [].

Consultare: http: // snia. org / sites / default / files / Multi-part% 20MIME% 20Extension% 20v1.0g.pdf

Descrive un modo per trasferire i dati binari tra un client e un server CDMI utilizzando operazioni di "tipo di contenuto CDMI" senza richiedere la conversione base64 dei dati binari.

Se è possibile utilizzare l'operazione "Tipo di contenuto non CDMI", è ideale per trasferire "dati" a / da un oggetto. I metadati possono quindi essere aggiunti / recuperati in / dall'oggetto come successiva operazione di "tipo di contenuto CDMI".

Se stai usando Node, penso che il modo più efficiente e semplice sia convertire in UTF16 con:

Buffer.from(data).toString('utf16le');

Puoi recuperare i tuoi dati tramite:

Buffer.from(s, 'utf16le');

Scavo un po 'di più (durante l'implementazione di base128 ) e lo espongo quando inviamo i caratteri i cui codici ascii sono più grandi di 128 quindi il browser (chrome) in effetti invia DUE caratteri (byte) invece uno :( . Il motivo è che JSON di default usa caratteri utf8 per i quali i caratteri con codici ASCII superiori a 127 sono codificati da due byte ciò che è stato menzionato dalla risposta chmike . Ho effettuato il test in questo modo: digita chrome url bar chrome: // net-export / , seleziona " Includi byte non elaborati " ;, avvia l'acquisizione, invia richieste POST (usando lo snippet in basso), interrompi l'acquisizione e salva il file json con i dati delle richieste non elaborate. Quindi guardiamo all'interno di quel json file:

  • Possiamo trovare la nostra richiesta base64 trovando la stringa 4142434445464748494a4b4c4d4e questa è una codifica esadecimale di ABCDEFGHIJKLMN e vedremo che " byte_count " ;: 639 per esso.
  • Siamo in grado di trovare la nostra richiesta above127 trovando la stringa C2BCC2BDC380C381C382C383C384C385C386C387C388C389C38AC38B sono codici utf8 esadecimali di richiesta ¼½ÀÁÂÃÄÅÊÇÈÉÊ> > c1c2c3c4c5c6c7c8c9cacbcccdce ). Il " byte_count " ;: 703 quindi è più lungo di 64 byte rispetto alla richiesta base64 perché i caratteri con codici ascii superiori a 127 sono codice di 2 byte nella richiesta :(

Quindi, in effetti, non abbiamo alcun profitto con l'invio di caratteri con codici > 127 :(. Per le stringhe base64 non osserviamo un comportamento così negativo (probabilmente anche per base85 - non lo controllo) - tuttavia potrebbe essere una soluzione per questo problema invierà i dati nella parte binaria dei dati POST multipart / form descritti in Æ risposta semplice (comunque di solito in questo caso non è necessario utilizzare alcuna codifica di base ...).

L'approccio alternativo può basarsi sulla mappatura di una porzione di dati di due byte in un carattere utf8 valido codificandolo utilizzando qualcosa come base65280 / base65k ma probabilmente sarebbe meno efficace di base64 a causa di specifica utf8 ...

function postBase64() {
  let formData = new FormData();
  let req = new XMLHttpRequest();

  formData.append("base64ch", "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/");
  req.open("POST", '/testBase64ch');
  req.send(formData);
}


function postAbove127() {
  let formData = new FormData();
  let req = new XMLHttpRequest();

  formData.append("above127", "¼½ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖ×ØÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõö÷øùúûüý");
  req.open("POST", '/testAbove127');
  req.send(formData);
}
<button onclick=postBase64()>POST base64 chars</button>
<button onclick=postAbove127()>POST chars with codes>127</button>

La mia soluzione ora, XHR2 sta usando ArrayBuffer. ArrayBuffer come sequenza binaria contiene contenuto multipart, video, audio, grafico, testo e così via con più tipi di contenuto. All in One Response.

Nel browser moderno, con DataView, StringView e Blob per diversi componenti. Vedi anche: http://rolfrost.de/video.html per maggiori dettagli.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top