Le format JSON ne prend pas nativement en charge les données binaires. Les données binaires doivent être échappées afin de pouvoir être placées dans un élément de chaîne (c'est-à-dire zéro ou plusieurs caractères Unicode entre guillemets en utilisant des échappements antislash) dans JSON.
Une méthode évidente pour échapper aux données binaires est d'utiliser Base64. Cependant, Base64 a une surcharge de traitement élevée. Il étend également 3 octets en 4 caractères, ce qui entraîne une augmentation de la taille des données d'environ 33%.
Un cas d'utilisation pour cela est le brouillon v0.8 de la spécification de l'API de stockage cloud CDMI . Vous créez des objets de données via un REST-Webservice en utilisant 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 meilleures méthodes et méthodes standard pour coder les données binaires en chaînes JSON?
JSON.parse
etc ......Réponses:
Il y a 94 caractères Unicode qui peuvent être représentés comme un octet selon la spécification JSON (si votre JSON est transmis en UTF-8). Dans cet esprit, je pense que le mieux que vous puissiez faire sur le plan spatial est base85 qui représente quatre octets en cinq caractères. Cependant, ce n'est qu'une amélioration de 7% par rapport à base64, son calcul est plus coûteux et les implémentations sont moins courantes que pour base64, ce n'est donc probablement pas une victoire.
Vous pouvez également simplement mapper chaque octet d'entrée au 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 l'efficacité de l'espace est mauvaise - une expansion de 105% (si tous les octets d'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 assez mauvais pour justifier un remplacement.
Voir aussi: Base91 et Base122
la source
base64.b85encode()
etb85decode()
maintenant. Une simple mesure d'encodage + décodage montre que b85 est plus de 13 fois plus lent que b64. Nous avons donc un gain de taille de 7%, mais une perte de performances de 1300%.DEL
comme un caractère de contrôle.J'ai rencontré le même problème et j'ai pensé 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 envoyez séparément sous forme de binaire brut (image (s), wav, etc.) indexé par le nom Content-Disposition .
Voici un joli tutoriel sur la façon de le faire dans obj-c, et voici un article de blog qui explique comment partitionner les données de chaîne avec la limite du formulaire et les séparer des données binaires.
Le seul changement que vous devez vraiment faire est du côté serveur; vous devrez capturer vos métadonnées qui devraient référencer les données binaires POSTées de manière appropriée (en utilisant une limite de disposition de contenu).
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.
À mon humble avis, l'envoi de données encodées en base64 est un hack; le RFC multipart / form-data a été créé pour des problèmes comme celui-ci: l'envoi de données binaires en combinaison avec du texte ou des métadonnées.
la source
Le problème avec UTF-8 est qu'il ne s'agit pas du codage le plus économe en espace. En outre, certaines séquences d'octets binaires aléatoires sont un codage UTF-8 non valide. Vous ne pouvez donc pas simplement interpréter une séquence d'octets binaires aléatoires comme des données UTF-8 car ce sera un codage UTF-8 non valide. L'avantage de cette contrainte sur le codage UTF-8 est qu'elle rend robuste et possible 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 le codage d'une valeur d'octet dans la plage [0..127] ne nécessitait qu'un seul octet dans le codage UTF-8, le codage d'une valeur d'octet dans la plage [128..255] nécessiterait 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. Les données binaires nécessiteraient donc une transformation pour être correctement codées.
Laisse 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 en un octet et l'autre moitié en deux octets. Les données binaires codées UTF-8 auraient 150% de la taille initiale.
L'encodage Base64 n'atteint que 133% de la taille initiale. L'encodage Base64 est donc plus efficace.
Qu'en est-il de l'utilisation d'un autre encodage Base? En UTF-8, l'encodage 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'augmenteraient que jusqu'à 114% de la taille initiale. Mieux que Base64. Malheureusement, nous ne pouvons pas utiliser cette astuce simple car JSON n'autorise pas certains caractères ASCII. Les 33 caractères de contrôle ASCII ([0..31] et 127) et le "et \ doivent être exclus. Cela ne nous laisse que 128-35 = 93 caractères.
Donc, en théorie, nous pourrions définir un codage Base93 qui augmenterait la taille codé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 morceaux de 6 bits pour lesquels une opération au niveau du bit simple fonctionne bien. Outre 133%, ce n'est pas beaucoup plus que 122%.
C'est pourquoi je suis arrivé indépendamment à la conclusion commune que Base64 est en effet le meilleur choix pour encoder des données binaires en JSON. Ma réponse en justifie. Je suis d'accord qu'il n'est pas très attrayant du point de vue des performances, mais considérez également l'avantage d'utiliser JSON avec sa représentation 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 de JSON. Mais avec JSON, ma conclusion est que Base64 est le meilleur.
la source
BSON (JSON binaire) peut fonctionner pour vous. http://en.wikipedia.org/wiki/BSON
Edit: pour info la bibliothèque .NET json.net prend en charge la lecture et l'écriture de bson si vous recherchez un peu d'amour côté serveur C #.
la source
Si vous rencontrez des problèmes de bande passante, essayez d'abord de compresser les données côté client, puis en base64-it.
Un bel exemple d'une telle magie est sur http://jszip.stuartk.co.uk/ et plus de discussion sur ce sujet est sur l' implémentation JavaScript de Gzip
la source
Content-Encoding
), car la base64 se comprime assez bien.yEnc peut fonctionner pour vous:
http://en.wikipedia.org/wiki/Yenc
Cependant, yEnc est un encodage 8 bits, donc le stocker dans une chaîne JSON a les mêmes problèmes que le stockage des données binaires d'origine - le faire de manière naïve signifie une expansion à 100%, ce qui est pire que base64.
la source
S'il est vrai que base64 a un taux d'expansion de ~ 33%, il n'est pas nécessairement vrai que le temps de traitement est bien plus que cela: cela dépend vraiment de la bibliothèque / boîte à outils JSON que vous utilisez. L'encodage et le décodage sont des opérations simples et directes, et ils peuvent même être optimisés pour l'encodage des caractères (car JSON ne prend en charge que UTF-8/16/32) - les caractères base64 sont toujours codés sur un octet pour les entrées de chaîne JSON. Par exemple, sur la plate-forme Java, il existe des bibliothèques qui peuvent faire le travail assez efficacement, de sorte que la surcharge est principalement due à une taille étendue.
Je suis d'accord avec deux réponses antérieures:
la source
Format du sourire
Il est très rapide à encoder, décoder et compacter
Comparaison de vitesse (basée sur java mais néanmoins significative): https://github.com/eishay/jvm-serializers/wiki/
C'est aussi une extension de JSON qui vous permet d'ignorer le codage base64 pour les tableaux d'octets
Les chaînes codées Smile peuvent être compressées lorsque l'espace est critique
la source
( Modifier 7 ans plus tard: Google Gears a disparu. 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:
Peut-être que vous pouvez tisser cela d'une manière ou d'une autre.
la source
Étant donné que vous recherchez la capacité de chausse-pied des données binaires dans un format strictement textuel et très limité, je pense que la surcharge de Base64 est minime par rapport à la commodité que vous vous attendez à maintenir avec JSON. Si la puissance de traitement et le débit sont un problème, vous devrez probablement reconsidérer vos formats de fichier.
la source
Juste pour ajouter le point de vue des ressources et de la complexité à la discussion. Depuis avoir fait PUT / POST et PATCH pour stocker de nouvelles ressources et les modifier, il faut se rappeler que le transfert de contenu est une représentation exacte du contenu qui est stocké et qui est 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. Cela s'explique de lui-même et c'est simple.
Et oui, JSON est quelque chose de paralysant, mais au final JSON lui-même est verbeux. Et la surcharge de mappage vers BASE64 est un moyen trop petit.
En utilisant correctement les messages en plusieurs parties, il faut soit démonter l'objet à envoyer, utiliser un chemin de propriété comme nom de paramètre pour la combinaison automatique, soit créer un autre protocole / format pour simplement exprimer la charge utile.
Aimant également l'approche BSON, ce n'est pas aussi largement et facilement pris en charge que l'on le souhaiterait.
Fondamentalement, nous manquons juste quelque chose ici, mais l'incorporation de données binaires car base64 est bien établie et est à faire à moins que vous n'ayez vraiment identifié la nécessité de faire le vrai transfert binaire (ce qui est rarement le cas).
la source
Je creuse un peu plus (lors de la mise en œuvre de base128 ), et expose que lorsque nous envoyons des caractères dont les codes ascii sont plus grands que 128, le navigateur (chrome) envoie en fait DEUX caractères (octets) au lieu d'un :( . La raison en est que JSON par défaut, utilisez les caractères utf8 pour lesquels les caractères avec des codes ascii supérieurs à 127 sont codés sur deux octets, ce qui a été mentionné par la réponse chmike . J'ai fait le test de cette manière: saisissez la barre d'url de chrome chrome: // net-export / , sélectionnez "Inclure raw octets ", lancez la capture, envoyez des requêtes POST (en utilisant un extrait en bas), arrêtez la capture et enregistrez le fichier json avec les données brutes des requêtes. Ensuite, nous regardons à l'intérieur de ce fichier json:
4142434445464748494a4b4c4d4e
codage est hexadécimalABCDEFGHIJKLMN
et nous le verrons"byte_count": 639
pour cela.C2BCC2BDC380C381C382C383C384C385C386C387C388C389C38AC38B
ce sont des codes de caractères utf8 request-hex¼½ÀÁÂÃÄÅÆÇÈÉÊË
(mais les codes hexadécimaux ascii de ces caractères le sontc1c2c3c4c5c6c7c8c9cacbcccdce
). Le"byte_count": 703
il est donc plus que 64bytes base64 demande parce que les caractères des codes ascii ci - dessus 127 sont le code de 2 octets demande :(Donc, en fait, nous n'avons aucun profit à envoyer des caractères avec des codes> 127 :(. Pour les chaînes en base64, nous n'observons pas un tel comportement négatif (probablement pour la base85 aussi - je ne le vérifie pas) - mais peut-être qu'une solution à ce problème sera envoyer des données dans une partie binaire de POST multipart / form-data décrites dans la réponse Ælex (cependant généralement dans ce cas nous n'avons pas besoin d'utiliser de codage de base du tout ...).
L'approche alternative peut s'appuyer sur le mappage d'une partie de données de deux octets en un seul caractère utf8 valide en le codant à l' aide de quelque chose comme base65280 / base65k, mais ce serait probablement moins efficace que base64 en raison de la spécification utf8 ...
Afficher l'extrait de code
la source
Le type de données est vraiment préoccupant. J'ai testé différents scénarios sur l'envoi de la charge utile à partir d'une ressource RESTful. Pour l'encodage, j'ai utilisé Base64 (Apache) et pour la compression GZIP (java.utils.zip. *). La charge utile 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é. Le contenu image et audio a été envoyé sous forme d'octets codés et compressés [].
la source
Voir: http://snia.org/sites/default/files/Multi-part%20MIME%20Extension%20v1.0g.pdf
Il décrit un moyen de transférer des données binaires entre un client CDMI et un serveur à l'aide d'opérations de type de contenu CDMI sans nécessiter de conversion 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 / récupérées vers / depuis l'objet en tant qu'opération de «type de contenu CDMI» ultérieure.
la source
Ma solution maintenant, XHR2 utilise ArrayBuffer. L'ArrayBuffer en tant que séquence binaire contient du contenu en plusieurs parties, vidéo, audio, graphique, texte, etc. avec plusieurs types de contenu. Réponse tout en un.
Dans un navigateur moderne, ayant DataView, StringView et Blob pour différents composants. Voir aussi: http://rolfrost.de/video.html pour plus de détails.
la source
[16, 2, 38, 89]
ce qui est très inefficace.