jQuery.parseJSON génère une erreur "JSON non valide" en raison d'un guillemet simple échappé dans JSON

202

Je fais des requêtes à mon serveur en utilisant jQuery.post()et mon serveur retourne des objets JSON (comme { "var": "value", ... }). Cependant, si l'une des valeurs contient un guillemet simple (correctement échappé comme \'), jQuery ne parvient pas à analyser une chaîne JSON par ailleurs valide. Voici un exemple de ce que je veux dire ( fait dans la console 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" }

Est-ce normal? N'y a-t-il aucun moyen de passer correctement un devis unique via JSON?

Felix
la source

Réponses:

325

Selon le diagramme de la machine d'état sur le site Web JSON , seuls les caractères entre guillemets d'échappement sont autorisés, pas les guillemets simples. Il n'est pas nécessaire d'échapper les caractères de guillemet simple:

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


Mise à jour - Plus d'informations pour ceux qui sont intéressés:


Douglas Crockford ne dit pas spécifiquement pourquoi la spécification JSON n'autorise pas les guillemets simples échappés dans les chaînes. Cependant, lors de sa discussion sur JSON dans l' annexe E de JavaScript: The Good Parts , il écrit:

Les objectifs de conception de JSON devaient être minimes, portables, textuels et un sous-ensemble de JavaScript. Moins nous devons nous mettre d'accord pour interopérer, plus facilement nous pouvons interopérer.

Il a donc peut-être décidé de ne permettre que la définition de chaînes à l'aide de guillemets doubles, car il s'agit d'une règle de moins sur laquelle toutes les implémentations JSON doivent se mettre d'accord. Par conséquent, il est impossible pour un caractère guillemet simple dans une chaîne de terminer accidentellement la chaîne, car par définition une chaîne ne peut être terminée que par un caractère guillemet double. Par conséquent, il n'est pas nécessaire d'autoriser l'échappement d'un seul caractère guillemet dans la spécification formelle.


Creuser un peu plus profond, de Crockford org.json la mise en œuvre de JSON pour Java est plus permissive et ne permet de guillemets simples:

Les textes produits par les méthodes toString sont strictement conformes aux règles de syntaxe JSON. Les constructeurs sont plus indulgents dans les textes qu'ils accepteront:

...

  • Les chaînes peuvent être citées avec '(guillemet simple).

Ceci est confirmé par le code source de JSONTokener . La nextStringméthode accepte les caractères de guillemet simple d'échappement et les traite comme des caractères de guillemet double:

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 haut de la méthode se trouve un commentaire informatif:

Le format JSON formel n'autorise pas les chaînes entre guillemets simples, mais une implémentation est autorisée à les accepter.

Ainsi, certaines implémentations accepteront des guillemets simples - mais vous ne devriez pas vous fier à cela. De nombreuses implémentations populaires sont assez restrictives à cet égard et rejetteront JSON qui contient des chaînes entre guillemets simples et / ou des guillemets simples échappés.


Enfin, pour rattacher cela à la question d'origine, essayez d'jQuery.parseJSON abord d'utiliser l'analyseur JSON natif du navigateur ou une bibliothèque chargée telle que json2.js, le cas échéant (qui sur une note latérale est la bibliothèque sur laquelle la logique jQuery est basée si JSONn'est pas définie) . Ainsi, jQuery ne peut être aussi permissif que cette implémentation sous-jacente:

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 );
},

Pour autant que je sache, ces implémentations adhèrent uniquement à la spécification JSON officielle et n'acceptent pas les guillemets simples, donc jQuery non plus.

Justin Ethier
la source
4
UPDATE :: JQuery est très restrictif lors du collage de JSON. Si vous essayez alert ($. ParseJSON ("[\" Ciao \\ '\ "]")); cela ne fonctionne pas à cause de ce que Justin a rapporté
daitangio
2
" L'implémentation JSON pour Java de Crockford par org.json est plus autorisée et autorise les guillemets simples " # C'est juste une bonne pratique: principe de robustesse
Duncan Jones
1
@DuncanJones - Cet article pourrait expliquer pourquoi aucun des navigateurs ne semble suivre ce principe en ce qui concerne JSON: joelonsoftware.com/items/2008/03/17.html
Justin Ethier
1
@JustinEthier, comme indiqué par cette réponse stackoverflow.com/a/25491642/759452 , la spécification JSON tools.ietf.org/html/rfc7159 dit Any character may be escaped, cela peut expliquer pourquoi certaines implémentations permettraient d'échapper les guillemets simples.
Adrien Be
1
@AdrienBe - Intéressant ... mais est-ce que cela signifiait qu'un caractère pouvait être échappé s'il était composé de 4 chiffres hexadécimaux? Selon le diagramme d'état ci-dessus et celui de la section 7 de la RFC, l'échappement d'une seule citation telle qu'elle \'est écrite n'est toujours pas autorisé. Ce serait bien si le RFC était plus explicite sur ce point.
Justin Ethier
15

Si vous avez besoin d'un guillemet simple à l'intérieur d'une chaîne, puisque \ 'n'est pas défini par la spécification, utilisez \u0027 voir http://www.utf8-chartable.de/ pour tous

modifier: veuillez excuser ma mauvaise utilisation du mot backticks dans les commentaires. Je voulais dire barre oblique inverse. Mon point ici est que dans le cas où vous avez des chaînes imbriquées dans d'autres chaînes, je pense qu'il peut être plus utile et plus lisible d'utiliser unicode au lieu de beaucoup de barres obliques inverses pour échapper à une seule citation. Si vous n'êtes pas imbriqué, il est vraiment plus facile de simplement y mettre une vieille citation.

slf
la source
29
Non. Utilisez simplement un guillemet simple.
Jeff Kaufman
Parfois, il est tout simplement plus facile d'utiliser unicode que des tonnes de tiques inverses. Particulièrement à l'intérieur des tiques arrière alternées.
slf
3
Pourquoi auriez-vous besoin de backticks? Si vous avez une chaîne comme "foo 'bar" ", vous ne laissez pas les guillemets simples sans échapper.
Jeff Kaufman
3
exactement ce que je cherchais. J'essaie d'écrire une chaîne json sur une page en tant que chaîne js var et de la mettre entre guillemets simples et elle se terminait tôt chaque fois qu'une valeur de propriété avait une seule guillemet. Maintenant, je fais juste un json.Replace ("'", "\ u0027") dans le code derrière avant de l'écrire sur la page.
Zack
@Zack Vous ne devez pas joindre JSON avec des guillemets. Si vous avez besoin d'une chaîne à partir d'une chaîne JSON existante, il suffit de la stringifier à nouveau. en PHP, ce serait là var jsonEncodedAsString = <?= json_encode(myEncodedJson) ?>myEncodedJsonest le résultat d'un précédent json_encodequi prendra soin d'échapper à votre guillemet simple, en fait, il produira simplement quelque chose d'une grosse chaîne entourée de guillemets doubles, donc les guillemets simples ne seront pas échappés, mais les guillemets doubles volonté.
Juan Mendes
5

Je comprends où se situe le problème et quand je regarde les spécifications, il est clair que les guillemets simples non échappés doivent être analysés correctement.

J'utilise la fonction jQuery.parseJSON de jquery pour analyser la chaîne JSON mais j'obtiens toujours l'erreur d'analyse lorsqu'il y a un seul guillemet dans les données préparées avec json_encode.

Serait-ce une erreur dans mon implémentation qui ressemble à ceci (PHP - côté serveur):

$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) . "]";

La dernière étape consiste à stocker la chaîne codée JSON dans une variable JS:

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

Si j'utilise "" au lieu de '', cela génère toujours une erreur.

SOLUTION:

La seule chose qui a fonctionné pour moi a été d'utiliser le masque bitmap JSON_HEX_APOS pour convertir les guillemets simples comme ceci:

json_encode($tmp, JSON_HEX_APOS);

Existe-t-il une autre façon de résoudre ce problème? Mon code est-il incorrect ou mal écrit?

Merci

Erik Čerpnjak
la source
'<? = $ marker; ?> 'ce n'est pas un json valide. Les guillemets environnants sont "mangés" par l'interpréteur javascript, laissant une chaîne qui commence par un <... ce que vous vouliez vraiment essayer était l'un des deux: jQuery.parseJSON ('"<? = $ Marker;?>" '); jQuery.parseJSON ("\" <? = $ marker;?> \ ""); Selon la spécification, les chaînes json doivent utiliser des guillemets doubles, mais javascript ne s'en soucie pas, vous avez donc soit une chaîne javascript à guillemet simple, soit un guillemet double, mais si vous utilisez cette dernière, vous devez leur échapper tous les utilise des guillemets doubles dans la chaîne.
Chris Cogdon
3

Lorsque vous envoyez un devis unique dans une requête

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

Lorsque vous obtenez la valeur, y compris un seul devis

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

Si vous souhaitez rechercher / insérer la valeur qui inclut un seul devis dans une requête xxx=Replace(empid,"'","''")

BehranG BinA
la source
1
Mais il n'est pas nécessaire d'échapper à une seule citation en la passant dans le cadre d'une chaîne JSON ...
Justin Ethier
2

Frapper un problème similaire en utilisant CakePHP pour sortir un bloc de script JavaScript en utilisant PHP natif json_encode. $contractorCompaniescontient des valeurs qui ont des guillemets simples et comme expliqué ci-dessus et attendu json_encode($contractorCompanies)ne leur échappe pas parce que son JSON valide.

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

En ajoutant des addlashes () autour de la chaîne encodée JSON, vous échappez ensuite les guillemets permettant à Cake / PHP de faire écho au javascript correct au navigateur. Les erreurs JS disparaissent.

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

J'essayais d'enregistrer un objet JSON d'une demande XHR dans un attribut HTML5 data- *. J'ai essayé plusieurs des solutions ci-dessus sans succès.

Ce que j'ai finalement fini par remplacer le guillemet simple 'par son code à l' &#39;aide d'une expression régulière après l'appel de la méthode stringify () de la manière suivante:

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
la source
0

Intéressant. Comment générez-vous votre JSON côté serveur? Utilisez-vous une fonction de bibliothèque (telle quejson_encode en PHP), ou construisez-vous la chaîne JSON à la main?

La seule chose qui attire mon attention est l'apostrophe d'échappement ( \'). Étant donné que vous utilisez des guillemets doubles, comme vous le devriez, il n'est pas nécessaire d'échapper aux guillemets simples. Je ne peux pas vérifier si c'est bien la cause de votre erreur jQuery, car je n'ai pas encore mis à jour moi-même la version 1.4.1.

Aistina
la source
J'utilise une bibliothèque en PHP pour générer l'objet JSON - un peu raté pour l'écrire. Merci d'avoir fait remarquer cela.
Erik Čerpnjak