Codificación de caracteres JSON: ¿UTF-8 es bien compatible con los navegadores o debo usar secuencias de escape numéricas?

89

Estoy escribiendo un servicio web que usa json para representar sus recursos, y estoy un poco atascado pensando en la mejor manera de codificar el json. Al leer json rfc ( http://www.ietf.org/rfc/rfc4627.txt ), queda claro que la codificación preferida es utf-8. Pero el rfc también describe un mecanismo de escape de cadenas para especificar caracteres. Supongo que esto generalmente se usaría para escapar de caracteres no ascii, lo que haría que el utf-8 resultante fuera ascii válido.

Entonces, digamos que tengo una cadena json que contiene caracteres Unicode (puntos de código) que no son ASCII. ¿Mi servicio web debería codificarlo en utf-8 y devolverlo, o debería escapar de todos esos caracteres que no son ascii y devolver ascii puro?

Me gustaría que los navegadores pudieran ejecutar los resultados usando jsonp o eval. ¿Eso afecta la decisión? Mi conocimiento de la compatibilidad con JavaScript de varios navegadores para utf-8 es deficiente.

EDITAR: Quería aclarar que mi principal preocupación sobre cómo codificar los resultados es realmente sobre el manejo de los resultados por parte del navegador. Lo que he leído indica que los navegadores pueden ser sensibles a la codificación cuando usan JSONP en particular. No he encontrado ninguna información realmente buena sobre el tema, así que tendré que empezar a hacer algunas pruebas para ver qué sucede. Idealmente, me gustaría escapar solo de esos pocos caracteres que se requieren y solo utf-8 codificar los resultados.

Schickb
fuente

Respuestas:

88

La especificación JSON requiere que los decodificadores admitan UTF-8. Como resultado, todos los decodificadores JSON pueden manejar UTF-8 tan bien como pueden manejar las secuencias de escape numéricas. Este también es el caso de los intérpretes de Javascript, lo que significa que JSONP también manejará el JSON codificado en UTF-8.

En cambio, la capacidad de los codificadores JSON para utilizar las secuencias de escape numéricas le ofrece más opciones. Una razón por la que puede elegir las secuencias de escape numéricas sería si un mecanismo de transporte entre su codificador y el descodificador previsto no es seguro para los sistemas binarios.

Otra razón por la que puede querer usar secuencias de escape numéricas es para evitar que aparezcan ciertos caracteres en la secuencia, como <, &y ", que pueden interpretarse como secuencias HTML si el código JSON se coloca sin escapar a HTML o un navegador lo interpreta incorrectamente como HTML . Esto puede ser una defensa contra la inyección de HTML o las secuencias de comandos entre sitios (nota: algunos caracteres DEBEN tener formato de escape en JSON, incluidos "y \).

Algunos marcos, incluida la implementación de JSON de PHP, siempre realizan las secuencias de escape numéricas en el lado del codificador para cualquier carácter fuera de ASCII. Esto está destinado a la máxima compatibilidad con mecanismos de transporte limitados y similares. Sin embargo, esto no debe interpretarse como una indicación de que los decodificadores JSON tienen un problema con UTF-8.

Entonces, supongo que podrías decidir cuál usar así:

  • Simplemente use UTF-8, a menos que su método de almacenamiento o transporte entre el codificador y el decodificador no sea binario seguro.

  • De lo contrario, use las secuencias de escape numéricas.

thomasrutter
fuente
1
"todos los decodificadores JSON pueden manejar UTF-8" Si bien esto es cierto para los navegadores, solo porque el estándar lo requiera no significa que todo el software de decodificación JSON admita UTF-8.
Michael Mior
7
"Todos los decodificadores JSON pueden manejar UTF-8" es literalmente cierto. Si algo no puede aceptar UTF-8, no es un decodificador JSON. Puede ser similar a un decodificador JSON, pero definitivamente no lo es.
thomasrutter
Supongo que eso depende de la definición de decodificador JSON que esté utilizando, pero es un buen punto :)
Michael Mior
La razón por la que RFC 8259 especifica el soporte UTF-8 como obligatorio es que es lo que el mundo estandarizó. Las especificaciones obsoletas anteriores definían cadenas como Unicode pero no especificaban qué codificación; implementaciones estandarizadas en UTF-8 de todos modos y la especificación actualizada refleja eso.
thomasrutter
El soporte UTF-8 no se especifica como obligatorio en ese RFC para ningún software en particular, hasta donde yo sé. La única mención de UTF-8 es que debe usarse como codificación para JSON intercambiado fuera de un sistema cerrado. Esto no implica que todos los decodificadores JSON (un lenguaje que no se usa en el RFC) deban admitir UTF-8.
Michael Mior
17

Tuve un problema ahí. Cuando codifico JSON una cadena con un carácter como "é", todos los navegadores devolverán la misma "é", excepto IE, que devolverá "\ u00e9".

Luego, con PHP json_decode (), fallará si encuentra "é", así que para Firefox, Opera, Safari y Chrome, tengo que llamar a utf8_encode () antes de json_decode ().

Nota: con mis pruebas, IE y Firefox están usando su objeto JSON nativo, otros navegadores usan json2.js.

Tim Tisdall
fuente
10
Probablemente quisiste decir utf8_encode(), php.net/manual/en/function.utf8-encode.php
Binyamin
4
Si IE no puede decodificar eso, es un error en cualquier decodificador JSON que esté usando. Todos los decodificadores JSON deben decodificar correctamente la forma codificada o no son decodificadores JSON. En cuanto a su problema con json_decode () con el é sin escape, es posible que el texto que está alimentando no sea UTF-8. Los decodificadores JSON siempre asumen UTF-8, incluso la implementación de PHP, aunque PHP normalmente no asume UTF-8 en muchas otras funciones. Hay otras codificaciones de caracteres que pueden incluir un é sin escape y verse idénticas en la pantalla, pero que no son UTF-8. La codificación en forma \ uXXXX es una solución a esto.
thomasrutter
Solo digo: JSON puede venir legalmente en cualquier codificación Unicode (UTF-8, UTF-16 BE / LE, UTF32 BE / LE, con o sin marcador de orden de bytes). Y dado que ASCII es un subconjunto de UTF-8, también puede venir en ASCII. Si los analizadores aceptan UTF-32, por ejemplo, no lo sé.
gnasher729
1
Eso es correcto, y no se requiere que los analizadores admitan nada más que UTF-8. De la especificación: "El texto JSON DEBE estar codificado en UTF-8, UTF-16 o UTF-32. La codificación predeterminada es UTF-8, y los textos JSON que están codificados en UTF-8 son interoperables en el sentido de que ser leído correctamente por el número máximo de implementaciones; hay muchas implementaciones que no pueden leer correctamente textos en otras codificaciones (como UTF-16 y UTF-32). Las implementaciones NO DEBEN agregar una marca de orden de bytes al comienzo de un texto JSON. "
thomasrutter
@thomasrutter La especificación que citó es antigua. La especificación actual dice: "El texto JSON intercambiado entre sistemas que no forman parte de un ecosistema cerrado DEBE estar codificado con UTF-8. Las especificaciones anteriores de JSON no requerían el uso de UTF-8 al transmitir texto JSON. Sin embargo, la gran mayoría de las implementaciones de software basadas en JSON han optado por utilizar la codificación UTF-8, en la medida en que es la única codificación que logra la interoperabilidad. Las implementaciones NO DEBEN agregar una marca de orden de bytes (U + FEFF) al comienzo de una transmisión en red Texto JSON " .
Remy Lebeau
12

ASCII ya no está en él. Usar codificación UTF-8 significa que no está usando codificación ASCII. Para lo que debe usar el mecanismo de escape es para lo que dice el RFC:

Todos los caracteres Unicode pueden colocarse entre comillas, excepto los caracteres que deben ser de escape: comillas, solidus inverso y los caracteres de control (U + 0000 a U + 001F)

caos
fuente
1
Si lee la cita que proporcionó, verá que no es necesario que escape todos los caracteres Unicode, solo algunos caracteres especiales. Pero debe codificar los resultados (preferiblemente con utf-8). Entonces, la pregunta es: "¿Por qué molestarse en escapar de los caracteres Unicode normales si está codificando utf-8?".
schickb
Además, una cadena codificada en ascii es un subconjunto puro de utf-8. Si utilizo el escape de json para todos los caracteres no ascii, el resultado es ascii y, por lo tanto, utf-8. Varias bibliotecas json (como python simplejson) tienen modos para forzar resultados ascii. Supongo que por una razón, como quizás la ejecución en los navegadores.
schickb
Cuando te molestas en escapar de los caracteres Unicode normales es en contextos donde son metacaracteres, como cadenas. (El fragmento de RFC que cité se trata de cadenas; lo siento, no estaba claro al respecto). No es necesario hacer salidas ASCII todo el tiempo; Creo que eso es más para depurar con navegadores rotos.
caos
7

Estaba enfrentando el mismo problema. Esto funciona para mi. Por favor, chequee esto.

json_encode($array,JSON_UNESCAPED_UNICODE);
Ankit Sewadik
fuente
Cabe señalar que lo anterior es PHP, ya que la pregunta no es de ninguna manera específica de PHP y solo habla sobre el servicio web que también puede no usar PHP (como los lectores más antiguos aún pueden recordar ...)
ntninja
1

Al leer json rfc ( http://www.ietf.org/rfc/rfc4627.txt ), queda claro que la codificación preferida es utf-8.

Para su información, RFC 4627 ya no es la especificación oficial de JSON. Fue obsoleto en 2014 por RFC 7159 , que luego quedó obsoleto en 2017 por RFC 8259 , que es la especificación actual.

RFC 8259 establece:

8.1. Codificación de caracteres

El texto JSON intercambiado entre sistemas que no son parte de un ecosistema cerrado DEBE estar codificado usando UTF-8 [RFC3629] .

Las especificaciones anteriores de JSON no requerían el uso de UTF-8 al transmitir texto JSON. Sin embargo, la gran mayoría de las implementaciones de software basadas en JSON han optado por utilizar la codificación UTF-8, en la medida en que es la única codificación que logra la interoperabilidad.

Las implementaciones NO DEBEN agregar una marca de orden de bytes (U + FEFF) al comienzo de un texto JSON transmitido en red. En aras de la interoperabilidad, las implementaciones que analizan textos JSON PUEDEN ignorar la presencia de una marca de orden de bytes en lugar de tratarla como un error.

Remy Lebeau
fuente
0

Tuve un problema similar con é char ... Creo que el comentario "es posible que el texto que estás alimentando no sea UTF-8" probablemente esté cerca de la marca aquí. Tengo la sensación de que la intercalación predeterminada en mi instancia era otra cosa hasta que me di cuenta y cambié a utf8 ... el problema es que los datos ya estaban allí, así que no estoy seguro de si convirtió los datos o no cuando los cambié, se muestra bien en mysql banco de trabajo. El resultado final es que php no codificará los datos con json, solo devuelve falso. No importa qué navegador use, ya que es el servidor que causa mi problema, php no analizará los datos en utf8 si este carácter está presente. Como digo, no estoy seguro si se debe a la conversión del esquema a utf8 después de que los datos estaban presentes o simplemente a un error de php. En este caso utilicejson_encode(utf8_encode($string));

Paul Smith
fuente