Pregunta

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. Además, expande 3 bytes en 4 caracteres, lo que aumenta el tamaño de los 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, por ejemplo,

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?

¿Fue útil?

Solución

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 cuanto al espacio es base85 que representa cuatro bytes como cinco personajes 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 podría 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 tan malo suficiente como para justificar el reemplazo.

Consulte también: Base91

Otros consejos

Me encontré con el mismo problema y pensé en compartir una solución: multipart/form-data.

Al enviar un formulario de varias partes, 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 Contenido -Disposición nombre.

Aquí hay un buen tutorial sobre cómo hacer esto en obj-c, y aquí está un artículo de blog que explica cómo particionar 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. Combine esto con la compresión gzip si lo desea.

En mi humilde opinión, enviar datos codificados en base64 es un hack; el RFC multipart / form-data fue creado para problemas como este: enviar datos binarios en combinación con texto o metadatos.

BSON (Binary JSON) puede funcionar para usted. http://en.wikipedia.org/wiki/BSON

Editar: Para su información, la biblioteca .NET json.net admite la lectura y escritura de bson si está buscando un poco de amor en el servidor C #.

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 de bytes binarios aleatorios 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 empecemos 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, caracteres de control, " y \ no pueden aparecer en una cadena. Por lo tanto, los datos binarios requerirían alguna transformación para codificarse correctamente.

Deja 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 el " y \ debe ser excluido. 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.

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 implementación de JavaScript de Gzip

yEnc podría funcionar para usted:

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

  

" yEnc es un esquema de codificación de binario a texto para transferir binarios   archivos en [texto]. Reduce los gastos generales sobre los anteriores basados ??en US-ASCII   métodos de codificación mediante el uso de un método de codificación ASCII extendido de 8 bits.   La sobrecarga de yEnc es a menudo (si cada valor de byte aparece aproximadamente   con la misma frecuencia en promedio) tan poco como 1 & # 8211; 2%, en comparación con   33% & # 8211; 40% de sobrecarga para métodos de codificación de 6 bits como uuencode y Base64.   ... En 2003, yEnc se convirtió en el sistema de codificación estándar de facto para   archivos binarios en Usenet. "

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 & # 8212; hacerlo de la manera ingenua significa una expansión del 100%, que es peor que la base64.

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 conjuntos de bytes

Las cadenas codificadas por sonrisa se pueden comprimir cuando el espacio es crítico

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 la decodificación son operaciones sencillas 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:

  • base64 es un estándar simple y de uso común, por lo que es poco probable que encuentre algo mejor específicamente para usar con JSON (base-85 es utilizado por PostScript, etc., pero los beneficios son, en el mejor de los casos, marginales cuando lo piensa)
  • la compresión antes de la codificación (y después de la decodificación) puede tener mucho sentido, dependiendo de los datos que use

( 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:

  

Blob API

     

JavaScript tiene un tipo de datos incorporado para cadenas de texto, pero nada para datos binarios. El objeto Blob intenta abordar esta limitación.

Tal vez puedas tejer eso de alguna manera.

Dado que 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.

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.

Usando los mensajes de varias partes correctamente, uno tiene que desmantelar el objeto a enviar, usar una ruta de propiedad como el nombre del parámetro para la combinación automática o necesitará crear otro protocolo / formato para simplemente 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 casi nunca es el caso).

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 [].

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 usar 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'.

Si está utilizando Node, creo que la forma más eficiente y fácil es convertir a UTF16 con:

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

Puede recuperar sus datos:

Buffer.from(s, 'utf16le');

Excavo un poco más (durante la implementación de base128 ), y expongo eso cuando enviamos caracteres qué códigos ASCII son mayores que 128, entonces el navegador (Chrome) de hecho envía DOS caracteres (bytes) en lugar de uno :( . La razón es que JSON por defecto usa 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 chmike respuesta. Hice la prueba de esta manera: escriba chrome url bar chrome: // net-export / , seleccione " Incluir bytes sin procesar " ;, 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 miraremos dentro de ese json archivo:

  • Podemos encontrar nuestra solicitud de base64 encontrando la cadena 4142434445464748494a4b4c4d4e esta es la codificación hexadecimal de ABCDEFGHIJKLMN y veremos que " byte_count " ;: 639 para ello.
  • Podemos encontrar nuestra solicitud anterior127 encontrando la cadena C2BCC2BDC380C381C382C383C384C385C386C387C388C389C38AC38B son códigos de caracteres utf8 request-hex utcode & # 188; & # 189; & # 192; & # 193; & # 194; & # 195; & # 196; & # 197; & # 198; & # 199; & # 200; & # 201; & # 202; & # 203; (sin embargo, los códigos hexadecimales ascii de este los caracteres son c1c2c3c4c5c6c7c8c9cacbcccdce ). El " byte_count " ;: 703 por lo que es 64bytes más largo que la solicitud base64 porque los caracteres con códigos ASCII superiores a 127 son código por 2 bytes en la solicitud :(

Entonces, de hecho, no tenemos ganancias al enviar caracteres con códigos > 127 :(. Para cadenas de base64 no observamos un comportamiento tan negativo (probablemente también para base85, no lo verifico); sin embargo, puede ser alguna solución para este problema enviará datos en la parte binaria de datos POST multipart / form descritos en & # 198; lex answer (sin embargo por lo general, en este caso, 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 especificación 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>

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. Respuesta todo en uno.

En el navegador moderno, que tiene DataView, StringView y Blob para diferentes componentes. Consulte también: http://rolfrost.de/video.html para obtener más detalles.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top