El formato JSON de forma nativa no admite datos binarios. Los datos binarios deben escaparse para que puedan colocarse en un elemento de cadena (es decir, cero o más caracteres Unicode en comillas dobles usando escapes de barra invertida) en JSON.
Un método obvio para escapar de datos binarios es usar Base64. Sin embargo, Base64 tiene una alta sobrecarga de procesamiento. También expande 3 bytes en 4 caracteres, lo que conduce a un mayor tamaño de datos en aproximadamente un 33%.
Un caso de uso para esto es el borrador v0.8 de la especificación de API de almacenamiento en la nube CDMI . Puede crear objetos de datos a través de un servicio web REST utilizando JSON, p. Ej.
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=",
}
¿Hay mejores formas y métodos estándar para codificar datos binarios en cadenas JSON?
JSON.parse
etc. ......Respuestas:
Hay 94 caracteres Unicode que se pueden representar como un byte de acuerdo con la especificación JSON (si su JSON se transmite como UTF-8). Con eso en mente, creo que lo mejor que puede hacer en el espacio es base85, que representa cuatro bytes como cinco caracteres. Sin embargo, esto es solo una mejora del 7% sobre base64, es más costoso de calcular y las implementaciones son menos comunes que para base64, por lo que probablemente no sea una victoria.
También puede simplemente asignar cada byte de entrada al carácter correspondiente en U + 0000-U + 00FF, luego hacer la codificación mínima requerida por el estándar JSON para pasar esos caracteres; La ventaja aquí es que la decodificación requerida es nula más allá de las funciones integradas, pero la eficiencia del espacio es mala: una expansión del 105% (si todos los bytes de entrada son igualmente probables) frente al 25% para base85 o 33% para base64.
Veredicto final: base64 gana, en mi opinión, con el argumento de que es común, fácil y no lo suficientemente malo como para justificar el reemplazo.
Vea también: Base91 y Base122
fuente
base64.b85encode()
yb85decode()
ahora. Una simple medición de tiempo de codificación + decodificación muestra que b85 es más de 13 veces más lento que b64. Por lo tanto, tenemos una ganancia de tamaño del 7%, pero pérdida de rendimiento del 1300%.DEL
ser un personaje de control.Me encontré con el mismo problema y pensé en compartir una solución: multipart / form-data.
Al enviar un formulario multiparte, primero envía como cadena sus metadatos JSON y luego lo envía por separado como binario sin procesar (imagen (es), wavs, etc.) indexado por el nombre de Content-Disposition .
Aquí hay un buen tutorial sobre cómo hacer esto en obj-c, y aquí hay un artículo de blog que explica cómo dividir los datos de la cadena con el límite del formulario y separarlos de los datos binarios.
El único cambio que realmente necesita hacer es en el lado del servidor; tendrá que capturar sus metadatos que deberían hacer referencia a los datos binarios POST'ed apropiadamente (mediante el uso de un límite de Disposición de contenido).
De acuerdo, requiere trabajo adicional en el lado del servidor, pero si está enviando muchas imágenes o imágenes grandes, vale la pena. Combina esto con la compresión gzip si quieres.
En mi humilde opinión, enviar datos codificados en base64 es un hack; El RFC multipart / form-data se creó para cuestiones como esta: enviar datos binarios en combinación con texto o metadatos.
fuente
El problema con UTF-8 es que no es la codificación más eficiente en cuanto al espacio. Además, algunas secuencias de bytes binarios aleatorios son codificaciones UTF-8 no válidas. Por lo tanto, no puede simplemente interpretar una secuencia aleatoria de bytes binarios como algunos datos UTF-8 porque será una codificación UTF-8 no válida. El beneficio de esta restricción en la codificación UTF-8 es que hace que sea robusto y posible localizar caracteres de varios bytes que comienzan y terminan cualquier byte que comencemos a mirar.
Como consecuencia, si codificar un valor de byte en el rango [0..127] necesitaría solo un byte en la codificación UTF-8, ¡codificar un valor de byte en el rango [128..255] requeriría 2 bytes! Peor que eso. En JSON, los caracteres de control "y \ no pueden aparecer en una cadena. Por lo tanto, los datos binarios requerirían alguna transformación para codificarse correctamente.
Vamos a ver. Si asumimos valores de bytes aleatorios distribuidos uniformemente en nuestros datos binarios, entonces, en promedio, la mitad de los bytes se codificarían en uno y la otra mitad en dos bytes. Los datos binarios codificados UTF-8 tendrían el 150% del tamaño inicial.
La codificación Base64 crece solo al 133% del tamaño inicial. Entonces, la codificación Base64 es más eficiente.
¿Qué pasa con el uso de otra codificación Base? En UTF-8, la codificación de los 128 valores ASCII es la más eficiente en cuanto al espacio. En 8 bits puede almacenar 7 bits. Entonces, si cortamos los datos binarios en fragmentos de 7 bits para almacenarlos en cada byte de una cadena codificada UTF-8, los datos codificados crecerían solo al 114% del tamaño inicial. Mejor que Base64. Desafortunadamente, no podemos usar este truco fácil porque JSON no permite algunos caracteres ASCII. Los 33 caracteres de control de ASCII ([0..31] y 127) y "y \ deben excluirse. Esto nos deja solo 128-35 = 93 caracteres.
Entonces, en teoría, podríamos definir una codificación Base93 que aumentaría el tamaño codificado a 8 / log2 (93) = 8 * log10 (2) / log10 (93) = 122%. Pero una codificación Base93 no sería tan conveniente como una codificación Base64. Base64 requiere cortar la secuencia de bytes de entrada en fragmentos de 6 bits para los cuales la operación simple a nivel de bits funciona bien. Además del 133% no es mucho más del 122%.
Es por eso que llegué independientemente a la conclusión común de que Base64 es de hecho la mejor opción para codificar datos binarios en JSON. Mi respuesta presenta una justificación para ello. Estoy de acuerdo en que no es muy atractivo desde el punto de vista del rendimiento, pero considere también el beneficio de usar JSON con su representación de cadena legible por humanos fácil de manipular en todos los lenguajes de programación.
Si el rendimiento es crítico, una codificación binaria pura debe considerarse como un reemplazo de JSON. Pero con JSON, mi conclusión es que Base64 es el mejor.
fuente
BSON (Binary JSON) puede funcionar para usted. http://en.wikipedia.org/wiki/BSON
Editar: FYI, la biblioteca .NET json.net admite la lectura y escritura de bson si está buscando un poco de amor en el servidor C #.
fuente
Si tiene problemas con el ancho de banda, primero intente comprimir los datos en el lado del cliente, luego base64-it.
Un buen ejemplo de tal magia está en http://jszip.stuartk.co.uk/ y más discusión sobre este tema está en la implementación de JavaScript de Gzip
fuente
Content-Encoding
), ya que base64 se comprime bastante bien.yEnc podría funcionar para usted:
http://en.wikipedia.org/wiki/Yenc
Sin embargo, yEnc es una codificación de 8 bits, por lo que almacenarla en una cadena JSON tiene los mismos problemas que almacenar los datos binarios originales: hacerlo de manera ingenua significa una expansión del 100%, que es peor que base64.
fuente
Si bien es cierto que base64 tiene una tasa de expansión de ~ 33%, no es necesariamente cierto que la sobrecarga de procesamiento sea significativamente mayor que esto: realmente depende de la biblioteca / kit de herramientas JSON que esté utilizando. La codificación y decodificación son operaciones simples y directas, e incluso se pueden optimizar la codificación de caracteres wrt (ya que JSON solo admite UTF-8/16/32): los caracteres base64 siempre son de un solo byte para las entradas de cadena JSON. Por ejemplo, en la plataforma Java hay bibliotecas que pueden hacer el trabajo de manera bastante eficiente, por lo que la sobrecarga se debe principalmente al tamaño expandido.
Estoy de acuerdo con dos respuestas anteriores:
fuente
Formato de sonrisa
Es muy rápido codificar, decodificar y compactar
Comparación de velocidad (basada en Java pero significativa): https://github.com/eishay/jvm-serializers/wiki/
También es una extensión de JSON que le permite omitir la codificación base64 para matrices de bytes
Las cadenas codificadas por sonrisa se pueden comprimir cuando el espacio es crítico
fuente
( Editar 7 años después: Google Gears se ha ido. Ignora esta respuesta).
El equipo de Google Gears se topó con el problema de la falta de tipos de datos binarios y ha intentado solucionarlo:
Tal vez puedas tejer eso de alguna manera.
fuente
Como está buscando la capacidad de calzar datos binarios en un formato estrictamente basado en texto y muy limitado, creo que la sobrecarga de Base64 es mínima en comparación con la comodidad que espera mantener con JSON. Si la potencia de procesamiento y el rendimiento son una preocupación, entonces probablemente deba reconsiderar sus formatos de archivo.
fuente
Solo para agregar el punto de vista de recursos y complejidad a la discusión. Como hacer PUT / POST y PATCH para almacenar nuevos recursos y modificarlos, uno debe recordar que la transferencia de contenido es una representación exacta del contenido que se almacena y se recibe emitiendo una operación GET.
Un mensaje de varias partes a menudo se usa como salvador, pero por razones de simplicidad y para tareas más complejas, prefiero la idea de dar el contenido como un todo. Se explica por sí mismo y es simple.
Y sí, JSON es algo paralizante, pero al final JSON en sí es detallado. Y la sobrecarga de mapeo a BASE64 es una forma de pequeña.
Al usar los mensajes de varias partes correctamente, uno tiene que desmantelar el objeto a enviar, usar una ruta de propiedad como nombre del parámetro para la combinación automática o necesitará crear otro protocolo / formato para expresar la carga útil.
También me gusta el enfoque BSON, esto no es tan amplio y fácil de soportar como a uno le gustaría que fuera.
Básicamente, simplemente echamos de menos algo aquí, pero la inserción de datos binarios como base64 está bien establecida y es un camino a seguir a menos que realmente haya identificado la necesidad de hacer la transferencia binaria real (que a menudo no es el caso).
fuente
Excavo un poco más (durante la implementación de base128 ), y expongo que cuando enviamos caracteres cuyos códigos ASCII son mayores que 128, el navegador (Chrome) de hecho envía DOS caracteres (bytes) en lugar de uno :( . La razón es que JSON por defecto use caracteres utf8 para los que los caracteres con códigos ASCII superiores a 127 están codificados por dos bytes, lo que fue mencionado por la respuesta de Chmike . Hice la prueba de esta manera: escriba chrome url bar chrome: // net-export / , seleccione "Incluir raw bytes ", comience a capturar, envíe solicitudes POST (usando un fragmento en la parte inferior), deje de capturar y guarde el archivo json con datos de solicitudes sin procesar. Luego miramos dentro de ese archivo json:
4142434445464748494a4b4c4d4e
es una codificación hexadecimalABCDEFGHIJKLMN
y lo veremos"byte_count": 639
por eso.C2BCC2BDC380C381C382C383C384C385C386C387C388C389C38AC38B
son códigos de caracteres request-hex utf8¼½ÀÁÂÃÄÅÆÇÈÉÊË
(sin embargo, los códigos hexadecimales ascii de estos caracteres sonc1c2c3c4c5c6c7c8c9cacbcccdce
). Por"byte_count": 703
lo tanto, es 64bytes más largo que la solicitud base64 porque los caracteres con códigos ascii superiores a 127 tienen un código de 2 bytes en la solicitud :(Entonces, de hecho, no tenemos ganancias al enviar caracteres con códigos> 127 :(. Para las cadenas de base64 no observamos un comportamiento tan negativo (probablemente también para base85, no lo verifico); sin embargo, puede haber alguna solución para este problema. envío de datos en la parte binaria de datos POST multiparte / formulario descritos en Ælex respuesta (sin embargo, en este caso generalmente no necesitamos usar ninguna codificación base ...).
El enfoque alternativo puede basarse en mapear la porción de datos de dos bytes en un carácter utf8 válido codificándolo usando algo como base65280 / base65k, pero probablemente sería menos efectivo que base64 debido a la especificación utf8 ...
Mostrar fragmento de código
fuente
El tipo de datos realmente preocupa. He probado diferentes escenarios sobre el envío de la carga desde un recurso RESTful. Para codificar he usado Base64 (Apache) y para compresión GZIP (java.utils.zip. *). La carga contiene información sobre una película, una imagen y un archivo de audio. Comprimí y codifiqué los archivos de imagen y audio que degradaron drásticamente el rendimiento. La codificación antes de la compresión resultó bien. El contenido de imagen y audio se envió como bytes codificados y comprimidos [].
fuente
Consulte: http://snia.org/sites/default/files/Multi-part%20MIME%20Extension%20v1.0g.pdf
Describe una forma de transferir datos binarios entre un cliente CDMI y un servidor utilizando operaciones de 'tipo de contenido CDMI' sin requerir la conversión de base64 de los datos binarios.
Si puede utilizar la operación 'Tipo de contenido no CDMI', es ideal transferir 'datos' a / desde un objeto. Los metadatos pueden luego agregarse / recuperarse a / desde el objeto como una operación posterior de 'tipo de contenido CDMI'.
fuente
Mi solución ahora, XHR2 está usando ArrayBuffer. El ArrayBuffer como secuencia binaria contiene contenido multiparte, video, audio, gráfico, texto, etc. con múltiples tipos de contenido. Todo en una respuesta.
En el navegador moderno, que tiene DataView, StringView y Blob para diferentes componentes. Ver también: http://rolfrost.de/video.html para más detalles.
fuente
[16, 2, 38, 89]
que es muy ineficiente.