jQuery.parseJSON arroja el error "JSON no válido" debido a una comilla simple escapada en JSON

202

Estoy haciendo solicitudes a mi servidor usando jQuery.post()y mi servidor está devolviendo objetos JSON (como { "var": "value", ... }). Sin embargo, si alguno de los valores contiene una comilla simple (como se escapó correctamente \'), jQuery no puede analizar una cadena JSON válida. Aquí hay un ejemplo de lo que quiero decir ( hecho en la consola de Chrome ):

data = "{ \"status\": \"success\", \"newHtml\": \"Hello \\\'x\" }";
eval("x = " + data); // { newHtml: "Hello 'x", status: "success" }

$.parseJSON(data); // Invalid JSON: { "status": "success", "newHtml": "Hello \'x" }

¿Esto es normal? ¿No hay forma de pasar correctamente una sola cita a través de JSON?

Felix
fuente

Respuestas:

325

Según el diagrama de máquina de estado en el sitio web de JSON , solo se permiten caracteres de comillas dobles escapadas, no comillas simples. Los caracteres de comillas simples no necesitan escapar:

http://www.json.org/string.gif


Actualización : más información para los interesados:


Douglas Crockford no dice específicamente por qué la especificación JSON no permite comillas simples escapadas dentro de cadenas. Sin embargo, durante su discusión sobre JSON en el Apéndice E de JavaScript: The Good Parts , escribe:

Los objetivos de diseño de JSON eran ser mínimos, portátiles, textuales y un subconjunto de JavaScript. Cuanto menos necesitemos acordar para interoperar, más fácilmente podremos interoperar.

Entonces, tal vez decidió permitir que las cadenas se definan utilizando comillas dobles, ya que esta es una regla menos en la que todas las implementaciones de JSON deben estar de acuerdo. Como resultado, es imposible que un carácter de comillas simples dentro de una cadena termine accidentalmente la cadena, porque, por definición, una cadena solo puede terminarse con un carácter de comillas dobles. Por lo tanto, no es necesario permitir el escape de un carácter de comilla simple en la especificación formal.


Cavar un poco más profundo, Crockford org.json implementación de JSON para Java es más permisible y lo hace permitir caracteres de comillas simples:

Los textos producidos por los métodos toString se ajustan estrictamente a las reglas de sintaxis JSON. Los constructores son más indulgentes en los textos que aceptarán:

...

  • Las cadenas se pueden citar con '(comilla simple).

Esto lo confirma el código fuente de JSONTokener . El nextStringmétodo acepta caracteres de comillas simples escapadas y los trata como caracteres de comillas dobles:

public String nextString(char quote) throws JSONException {
    char c;
    StringBuffer sb = new StringBuffer();
    for (;;) {
        c = next();
        switch (c) {

        ...

        case '\\':
            c = this.next();
            switch (c) {

            ...

            case '"':
            case '\'':
            case '\\':
            case '/':
                sb.append(c);
                break;
        ...

En la parte superior del método hay un comentario informativo:

El formato JSON formal no permite cadenas en comillas simples, pero una implementación puede aceptarlas.

Por lo tanto, algunas implementaciones aceptarán comillas simples, pero no debe confiar en esto. Muchas implementaciones populares son bastante restrictivas a este respecto y rechazarán JSON que contiene cadenas entre comillas simples y / o comillas simples escapadas.


Finalmente, para vincular esto con la pregunta original, jQuery.parseJSONprimero intenta usar el analizador JSON nativo del navegador o una biblioteca cargada como json2.js cuando corresponda (que en una nota al margen es la biblioteca en la que se basa la lógica jQuery si JSONno está definida) . Por lo tanto, jQuery solo puede ser tan permisivo como esa implementación subyacente:

parseJSON: function( data ) {
    ...

    // Attempt to parse using the native JSON parser first
    if ( window.JSON && window.JSON.parse ) {
        return window.JSON.parse( data );
    }

    ...

    jQuery.error( "Invalid JSON: " + data );
},

Hasta donde sé, estas implementaciones solo se adhieren a la especificación JSON oficial y no aceptan comillas simples, por lo tanto, jQuery tampoco.

Justin Ethier
fuente
44
ACTUALIZACIÓN :: JQuery es muy restrictivo al pegar JSON. Si intenta alertar ($. ParseJSON ("[\" Ciao \\ '\ "]")); no funciona debido a lo que informó Justin
daitangio
2
" La implementación org.json de Crockford de JSON para Java es más permisible y permite caracteres de comillas simples " # Eso es solo una buena práctica: principio de robustez
Duncan Jones
1
@DuncanJones: este artículo puede proporcionar una idea de por qué ninguno de los navegadores parece seguir ese principio con respecto a JSON: joelonsoftware.com/items/2008/03/17.html
Justin Ethier
1
@JustinEthier como se señala en esta respuesta stackoverflow.com/a/25491642/759452 , la especificación JSON tools.ietf.org/html/rfc7159 dice Any character may be escaped, esto puede explicar por qué alguna implementación permitiría que se escapen las comillas simples.
Adrien Be
1
@AdrienBe - Interesante ... pero ¿querían decir que cualquier personaje puede escapar si consta de 4 dígitos hexadecimales? De acuerdo con el diagrama de estado anterior y el de la sección 7 del RFC, el escape de una sola cita como \'está escrito todavía no está permitido. Sería bueno si el RFC fuera más explícito en este punto.
Justin Ethier el
15

Si necesita una comilla simple dentro de una cadena, ya que \ 'no está definida por la especificación, use \u0027 ver http://www.utf8-chartable.de/ para ver todas

editar: disculpe mi mal uso de la palabra backticks en los comentarios. Me refería a la barra invertida. Mi punto aquí es que en el caso de que haya anidado cadenas dentro de otras cadenas, creo que puede ser más útil y legible usar unicode en lugar de muchas barras invertidas para escapar de una comilla simple. Sin embargo, si no está anidado, es realmente más fácil simplemente poner una cita antigua simple.

slf
fuente
29
No. Solo usa una comilla simple.
Jeff Kaufman,
A veces, es simplemente más fácil de usar Unicode que toneladas de back-ticks. Particularmente cuando está dentro alternando las garrapatas.
slf
3
¿Por qué necesitarías backticks? Si tiene una cadena como "foo 'bar'", simplemente deje las comillas simples sin escapes.
Jeff Kaufman
3
Exactamente lo que estaba buscando. Estoy tratando de escribir una cadena json en una página como una cadena js var y encerrarla entre comillas simples y terminaba temprano cada vez que un valor de propiedad tenía una comilla simple. Ahora solo hago un json.Replace ("'", "\ u0027") en el código detrás antes de escribirlo en la página.
Zack
@Zack No debe incluir JSON con comillas. Si necesita una cadena de una cadena JSON existente, simplemente vuelva a clasificarla. en PHP, que sería var jsonEncodedAsString = <?= json_encode(myEncodedJson) ?>donde myEncodedJsones el resultado de una anterior json_encodeque se hará cargo de escapar de su comilla simple, en realidad, lo hará justo cotizaciones de salida algo una gran cadena envuelta entre comillas dobles, por lo que no se escaparon comillas simples, dobles, pero será.
Juan Mendes
5

Entiendo dónde radica el problema y cuando miro las especificaciones está claro que las comillas simples sin escape deben analizarse correctamente.

Estoy usando la función jQuery.parseJSON de jquery para analizar la cadena JSON pero sigo obteniendo el error de análisis cuando hay una comilla simple en los datos que se preparan con json_encode.

¿Podría ser un error en mi implementación que se ve así (PHP - lado del servidor):

$data = array();

$elem = array();
$elem['name'] = 'Erik';
$elem['position'] = 'PHP Programmer';
$data[] = json_encode($elem);

$elem = array();
$elem['name'] = 'Carl';
$elem['position'] = 'C Programmer';
$data[] = json_encode($elem);

$jsonString = "[" . implode(", ", $data) . "]";

El último paso es que almaceno la cadena codificada JSON en una variable JS:

<script type="text/javascript">
employees = jQuery.parseJSON('<?=$marker; ?>');
</script>

Si uso "" en lugar de '', todavía arroja un error.

SOLUCIÓN:

Lo único que funcionó para mí fue usar bitmask JSON_HEX_APOS para convertir las comillas simples de esta manera:

json_encode($tmp, JSON_HEX_APOS);

¿Hay otra forma de abordar este problema? ¿Mi código está mal o mal escrito?

Gracias

Erik Čerpnjak
fuente
'<? = $ marcador; ?> 'esto no es válido json. El intérprete de javascript "come" las comillas circundantes, dejando una cadena que comienza con un <... lo que realmente quería probar era cualquiera de estos: jQuery.parseJSON ('"<? = $ Marker;?>" '); jQuery.parseJSON ("\" <? = $ marker;?> \ ""); De acuerdo con la especificación, las cadenas json deben usar comillas dobles, pero a javascript no le importa, entonces, o bien tiene una cadena javascript de comillas simples o una comilla doble, pero si usa la última, debe escapar a todas las usos de comillas dobles dentro de la cadena.
Chris Cogdon
3

Cuando envía una cotización simple en una consulta

empid = " T'via"
empid =escape(empid)

Cuando obtienes el valor incluyendo una cotización simple

var xxx  = request.QueryString("empid")
xxx= unscape(xxx)

Si desea buscar / insertar el valor que incluye una comilla simple en una consulta xxx=Replace(empid,"'","''")

BehranG BinA
fuente
1
Pero no hay necesidad de escapar de una sola cita al pasarla como parte de una cadena JSON ...
Justin Ethier
2

Llama la atención un problema similar usando CakePHP para generar un bloque de script JavaScript usando el nativo de PHP json_encode. $contractorCompaniescontiene valores que tienen comillas simples y, como se explicó anteriormente y se espera, json_encode($contractorCompanies)no se les escapa porque es un JSON válido.

<?php $this->Html->scriptBlock("var contractorCompanies = jQuery.parseJSON( '".(json_encode($contractorCompanies)."' );"); ?>

Al agregar addlashes () alrededor de la cadena codificada JSON, entonces escapa de las comillas permitiendo que Cake / PHP haga eco del javascript correcto en el navegador. Los errores de JS desaparecen.

<?php $this->Html->scriptBlock("var contractorCompanies = jQuery.parseJSON( '".addslashes(json_encode($contractorCompanies))."' );"); ?>
Chopstik
fuente
1

Intenté guardar un objeto JSON de una solicitud XHR en un atributo HTML5 data- *. Probé muchas de las soluciones anteriores sin éxito.

Lo que finalmente terminé haciendo fue reemplazar la comilla simple 'con su código &#39;usando una expresión regular después de que el método stringify () llamara de la siguiente manera:

var productToString = JSON.stringify(productObject);
var quoteReplaced = productToString.replace(/'/g, "&#39;");
var anchor = '<a data-product=\'' + quoteReplaced + '\' href=\'#\'>' + productObject.name + '</a>';
// Here you can use the "anchor" variable to update your DOM element.
BoCyrill
fuente
0

Interesante. ¿Cómo estás generando tu JSON en el servidor? ¿Está utilizando una función de biblioteca (comojson_encode en PHP), o está creando la cadena JSON a mano?

Lo único que me llama la atención es el apóstrofe de escape ( \'). Como está usando comillas dobles, como debería, no hay necesidad de escapar de comillas simples. No puedo verificar si esa es realmente la causa de su error de jQuery, ya que todavía no he actualizado a la versión 1.4.1.

Aistina
fuente
Yo uso una biblioteca en PHP para generar el objeto JSON, un poco perdido para escribir eso. Gracias por señalar eso.
Erik Čerpnjak