Question

Le format JSON , de manière native, ne prend pas en charge les données binaires. Les données binaires doivent être échappées pour pouvoir être placées dans un élément de chaîne (c'est-à-dire zéro caractère Unicode ou plus entre guillemets à l'aide de caractères d'échappement de barre oblique inversée) en JSON.

Une méthode évidente pour échapper aux données binaires consiste à utiliser Base64. Cependant, Base64 a une surcharge de traitement élevée. De plus, il étend 3 octets en 4 caractères, ce qui entraîne une augmentation de la taille des données d'environ 33%.

Le brouillon v0.8 de la spécification de l'API de stockage en nuage CDMI constitue un exemple de cette utilisation. . Vous créez des objets de données via un service Web REST à l'aide de JSON, par exemple.

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

Existe-t-il de meilleurs moyens et méthodes standard pour coder des données binaires en chaînes JSON?

Était-ce utile?

La solution

Il y a 94 caractères Unicode qui peuvent être représentés sous la forme d'un octet conformément à la spécification JSON (si votre JSON est transmis au format UTF-8). Dans cet esprit, je pense que le mieux que vous puissiez faire en matière d'espace est la base85 , qui représente quatre octets. comme cinq personnages. Cependant, il ne s’agit que d’une amélioration de 7% par rapport à base64, son calcul est plus coûteux et les implémentations étant moins courantes que pour base64, ce n’est donc probablement pas une victoire.

Vous pouvez également simplement mapper chaque octet d'entrée sur le caractère correspondant dans U + 0000-U + 00FF, puis effectuer l'encodage minimum requis par la norme JSON pour transmettre ces caractères; L'avantage ici est que le décodage requis est nul au-delà des fonctions intégrées, mais que le gain d'espace est mauvais - une expansion de 105% (si tous les octets en entrée sont également probables) contre 25% pour base85 ou 33% pour base64.

Verdict final: base64 gagne, à mon avis, au motif qu’il est commun, facile et pas mal suffisant pour justifier un remplacement.

Voir aussi: Base91

Autres conseils

J'ai rencontré le même problème et je pensais partager une solution: multipart / form-data.

En envoyant un formulaire en plusieurs parties, vous envoyez d'abord sous forme de chaîne vos métadonnées JSON , puis séparément sous forme de fichier binaire brut (image (s), wavs, etc.) indexé par le Contenu. -Disposition nom.

Voici un tutoriel sur la procédure à suivre pour l'obtenir dans obj-c, et voici un article de blog explique comment partitionner les données de chaîne avec la limite de formulaire et les séparer des données binaires.

Le seul changement que vous devez réellement faire est du côté serveur; vous devrez capturer vos métadonnées qui référenceront les données binaires POSTées de manière appropriée (en utilisant une limite Contenu-Disposition).

Certes, cela nécessite un travail supplémentaire côté serveur, mais si vous envoyez de nombreuses images ou de grandes images, cela en vaut la peine. Combinez cela avec la compression gzip si vous le souhaitez.

IMHO envoyer des données encodées en base64 est un hack; RFC multipart / form-data a été créé pour des problèmes tels que celui d’envoyer des données binaires en combinaison avec du texte ou des méta-données.

BSON (JSON binaire) peut fonctionner pour vous. http://en.wikipedia.org/wiki/BSON

Modifier: Pour votre information, la bibliothèque .NET json.net prend en charge la lecture et l’écriture de bson si vous recherchez un amour côté serveur C #.

Le problème avec UTF-8 est que ce n’est pas le codage le moins encombrant. De plus, certaines séquences d’octets binaires aléatoires ne sont pas valables pour le codage UTF-8. Par conséquent, vous ne pouvez pas interpréter une séquence d'octets binaires aléatoires comme des données UTF-8 car il s'agira d'un encodage UTF-8 non valide. L’intérêt de cette contrainte sur le codage UTF-8 est qu’il permet de localiser le début et la fin des caractères multi-octets, quel que soit l’octet que nous commençons à regarder.

Par conséquent, si coder une valeur d'octet dans la plage [0..127] n'aurait besoin que d'un octet en codage UTF-8, coder une valeur d'octet dans la plage [128..255] exigerait 2 octets! Pire que ça. En JSON, les caractères de contrôle, " et \ ne sont pas autorisés à apparaître dans une chaîne. Ainsi, les données binaires nécessiteraient une certaine transformation pour être correctement codées.

Laissez voir. Si nous supposons des valeurs d'octets aléatoires uniformément réparties dans nos données binaires, alors, en moyenne, la moitié des octets serait codée dans un octet et l'autre moitié dans deux octets. Les données binaires codées en UTF-8 auraient 150% de la taille initiale.

L'encodage Base64 n'augmente que jusqu'à 133% de la taille initiale. Le codage Base64 est donc plus efficace.

Qu'en est-il de l'utilisation d'un autre encodage Base? En UTF-8, le codage des 128 valeurs ASCII est le plus économe en espace. En 8 bits, vous pouvez stocker 7 bits. Donc, si nous coupons les données binaires en morceaux de 7 bits pour les stocker dans chaque octet d'une chaîne codée UTF-8, les données codées n'augmenteront que jusqu'à 114% de la taille initiale. Mieux que Base64. Malheureusement, nous ne pouvons pas utiliser cette astuce car JSON n'autorise pas certains caractères ASCII. Les 33 caractères de contrôle de l’ASCII ([0..31] et 127) et le caractère " et \ doivent être exclus. Cela ne nous laisse que 128-35 = 93 caractères.

Donc, en théorie, nous pourrions définir un encodage Base93 qui augmenterait la taille encodée à 8 / log2 (93) = 8 * log10 (2) / log10 (93) = 122%. Mais un encodage Base93 ne serait pas aussi pratique qu'un encodage Base64. Base64 nécessite de couper la séquence d'octets d'entrée en blocs de 6 bits pour lesquels une opération simple au niveau du bit fonctionne bien. À part 133% n’est pas beaucoup plus que 122%.

C’est pourquoi j’ai indépendamment conclu à la conclusion commune que Base64 était le meilleur choix pour coder des données binaires en JSON. Ma réponse présente une justification pour cela. Je conviens que cela n’est pas très intéressant du point de vue des performances, mais considérons également l’avantage d’utiliser JSON avec sa représentation sous forme de chaîne lisible par l’homme, facile à manipuler dans tous les langages de programmation.

Si les performances sont critiques, un codage binaire pur doit être considéré comme un remplacement du code JSON. Mais avec JSON, ma conclusion est que Base64 est le meilleur.

Si vous rencontrez des problèmes de bande passante, essayez d'abord de compresser les données côté client, puis base64-it.

Vous trouverez un bon exemple de cette magie à l'adresse http://jszip.stuartk.co.uk/ . Pour plus d'informations sur ce sujet, consultez la implémentation JavaScript de Gzip

.

yEnc pourrait fonctionner pour vous:

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

  

"yEnc est un schéma de codage binaire en texte pour transférer des données binaires.   fichiers dans [texte]. Il réduit les frais généraux par rapport aux précédents systèmes basés sur US-ASCII.   méthodes de codage en utilisant une méthode de codage ASCII étendu de 8 bits.   La surcharge de yEnc est souvent (si chaque valeur d’octet apparaît approximativement   avec la même fréquence en moyenne) aussi peu que 1 & # 8211; 2%, par rapport à   33% de surcoût pour les méthodes de codage 6 bits telles que uuencode et Base64.   ... En 2003, yEnc est devenu le système de codage standard de facto pour   fichiers binaires sur Usenet. "

Cependant, yEnc est un codage sur 8 bits. Par conséquent, le stocker dans une chaîne JSON pose les mêmes problèmes que le stockage des données binaires originales & # 8212; le faire de façon naïve signifie une expansion de 100%, ce qui est pire que base64.

Format du sourire

C’est très rapide d’encoder, décoder et compacter

Comparaison de la vitesse (basé sur Java mais néanmoins significatif): https://github.com/eishay/jvm-serializers/ wiki /

C'est également une extension de JSON qui vous permet de sauter l'encodage en base64 des tableaux d'octets

Les chaînes Smile peuvent être compressées lorsque l’espace est critique

S'il est vrai que base64 a un taux d’expansion d’environ 33%, il n’est pas forcément vrai que les frais de traitement sont considérablement plus importants que cela: cela dépend vraiment de la bibliothèque JSON / toolkit que vous utilisez. Le codage et le décodage sont des opérations simples et simples, et ils peuvent même être optimisés pour le codage de caractères (JSON ne prenant en charge que UTF-8/16/32) - les caractères base64 sont toujours à octet unique pour les entrées de chaîne JSON. Par exemple, sur la plate-forme Java, certaines bibliothèques peuvent effectuer le travail de manière assez efficace, de sorte que les frais généraux sont principalement dus à une taille étendue.

Je suis d'accord avec deux réponses précédentes:

  • base64 est un standard simple et couramment utilisé. Il est donc peu probable que vous trouviez quelque chose de mieux à utiliser spécifiquement avec JSON (la base 85 est utilisée en post-scriptum, etc.; mais les avantages sont au mieux marginaux lorsque vous y réfléchissez)
  • la compression avant l'encodage (et après le décodage) peut sembler logique, selon les données que vous utilisez

( Modifier 7 ans plus tard: Google Gears est parti. Ignorez cette réponse.)

L'équipe Google Gears a rencontré le problème du manque de types de données binaires et a tenté de le résoudre:

  

API Blob

     

JavaScript a un type de données intégré pour les chaînes de texte, mais rien pour les données binaires. L'objet Blob tente de remédier à cette limitation.

Peut-être que vous pouvez y insérer quelque chose.

Puisque vous recherchez la possibilité de regrouper les données binaires dans un format strictement limité au texte, je pense que les frais généraux de Base64 sont minimes par rapport à la commodité que vous vous attendez à obtenir avec JSON. Si la puissance de traitement et le débit vous préoccupent, vous devrez probablement reconsidérer les formats de fichier.

Juste pour ajouter le point de vue des ressources et de la complexité à la discussion. Depuis que vous utilisez PUT / POST et PATCH pour stocker de nouvelles ressources et les modifier, rappelez-vous que le transfert de contenu est une représentation exacte du contenu stocké et reçu en émettant une opération GET.

Un message en plusieurs parties est souvent utilisé comme sauveur, mais pour des raisons de simplicité et pour des tâches plus complexes, je préfère l’idée de donner le contenu dans son ensemble. C'est explicite et c'est simple.

Et oui, JSON est quelque chose de handicapant, mais au final, JSON lui-même est prolixe. Et la surcharge du mappage vers BASE64 est un moyen de gagner du temps.

Si vous utilisez correctement les messages en plusieurs parties, vous devez démanteler l'objet à envoyer, utiliser un chemin de propriété comme nom de paramètre pour la combinaison automatique ou créer un autre protocole / format pour exprimer simplement la charge utile.

Tout comme l’approche BSON, cette solution n’est pas aussi largement et facilement supportée qu’on le voudrait.

En gros, il nous manque quelque chose ici, mais intégrer des données binaires comme base64 est bien établi et reste une voie à suivre à moins d’avoir vraiment identifié le besoin de faire le vrai transfert binaire (ce qui est rarement le cas).

Le type de données concerne vraiment. J'ai testé différents scénarios lors de l'envoi de la charge depuis une ressource RESTful. Pour l'encodage, j'ai utilisé Base64 (Apache) et pour la compression GZIP (java.utils.zip. *). Le payload contient des informations sur le film, une image et un fichier audio. J'ai compressé et encodé les fichiers image et audio, ce qui a considérablement dégradé les performances. L'encodage avant la compression s'est bien passé. Les images et le contenu audio ont été envoyés sous forme d'octets codés et compressés [].

Voir: http: // snia. org / sites / default / files / Plusieurs parties% 20MIME% 20Extension% 20v1.0g.pdf

Il décrit un moyen de transférer des données binaires entre un client et un serveur CDMI à l'aide d'opérations "Type de contenu CDMI" sans nécessiter la conversion en base64 des données binaires.

Si vous pouvez utiliser l'opération "Type de contenu non-CDMI", il est idéal de transférer des "données" vers / depuis un objet. Les métadonnées peuvent ensuite être ajoutées / extraites ultérieurement à / de l'objet en tant qu'opération 'Type de contenu CDMI' ultérieure.

Si vous utilisez Node, je pense que le moyen le plus efficace et le plus simple consiste à convertir en UTF16 avec:

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

Vous pouvez récupérer vos données en:

Buffer.from(s, 'utf16le');

Je creuse un peu plus (lors de la mise en œuvre de base128 ) et l'expose lorsque nous envoyons des caractères les codes ascii sont supérieurs à 128, puis le navigateur (chrome) envoie en réalité DEUX caractères (octets) au lieu d’un: . La raison en est que JSON par défaut utilise des caractères utf8 pour lesquels les caractères avec des codes ascii supérieurs à 127 sont codés par deux octets, ce qui était mentionné par chmike , j'ai fait le test de cette manière: tapez chrome url bar chrome: // net-export / , sélectionnez "Inclure les octets bruts", lancez la capture, envoyez des demandes POST (en utilisant l'extrait de code au bas), arrêtez la capture et enregistrez le fichier JSON avec les données des demandes brutes. fichier:

  • Nous pouvons trouver notre requête en base64 en recherchant la chaîne 4142434445464748494a4b4c4d4e c'est un codage hexadécimal de ABCDEFGHIJKLMN et nous verrons que " byte_count " 639 pour cela.
  • Nous pouvons trouver notre requête ci-dessus127 en recherchant la chaîne C2BCC2BDC380C382C382C383C384C385C386C386C387C388C389C38AC38B . c1c2c3c4c5c6c7c8c9cacbcccdce ). Le "byte_count": 703 est donc plus long de 64 octets que la requête base64 car les caractères avec des codes ascii supérieurs à 127 sont codés sur 2 octets dans la requête: (

Nous n’avons donc aucun intérêt à envoyer des caractères avec les codes > 127 :(. Pour les chaînes en base64, nous n’observons pas un tel comportement négatif (probablement aussi pour base85 - je ne le vérifie pas). ce problème sera l'envoi de données dans la partie binaire du POST multipart / form-data décrit dans lex réponse (mais généralement dans ce cas nous n'avons pas du tout besoin d'utiliser un code de base ...).

L’approche alternative peut reposer sur le mappage d’une partie de données de deux octets en un caractère utf8 valide code par code en utilisant quelque chose comme base65280 / base65k , mais serait probablement moins efficace que base64 en raison de Spécification 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>

Ma solution maintenant, XHR2 utilise ArrayBuffer. ArrayBuffer en tant que séquence binaire contient du contenu en plusieurs parties, de la vidéo, de l'audio, des graphiques, du texte, etc., avec plusieurs types de contenu. Réponse tout en un.

Dans les navigateurs modernes, DataView, StringView et Blob ont différents composants. Voir aussi: http://rolfrost.de/video.html pour plus de détails.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top