Comment gérer les sauts de ligne dans JSON?

289

J'ai généré du JSON et j'essaye de le tirer dans un objet en JavaScript. Je continue à recevoir des erreurs. Voici ce que j'ai:

var data = '{"count" : 1, "stack" : "sometext\n\n"}';
var dataObj = eval('('+data+')');

Cela me donne une erreur:

unterminated string literal

Avec JSON.parse(data), je vois des messages d'erreur similaires: " Unexpected token ↵" dans Chrome et " unterminated string literal" dans Firefox et IE.

Lorsque je retire l' \naprès, sometextl'erreur disparaît dans les deux cas. Je n'arrive pas à comprendre pourquoi les \nmarques evalet JSON.parseéchouent.

ours polaire
la source
19
Essayez d'utiliser un véritable analyseur json au lieu de eval.
Eric

Réponses:

368

Je suppose que c'est ce que vous voulez:

var data = '{"count" : 1, "stack" : "sometext\\n\\n"}';

(Vous devez échapper le "\" dans votre chaîne (en le transformant en double - "\"), sinon il deviendra une nouvelle ligne dans la source JSON, pas les données JSON.)

BlaM
la source
101
C'est bien sûr correct, mais j'aimerais ajouter la raison de cette opération: la spécification JSON sur ietf.org/rfc/rfc4627.txt contient cette phrase dans la section 2.5: "Tous les caractères Unicode peuvent être placés dans le entre guillemets, sauf pour les caractères devant être échappés: guillemet, inversé et les caractères de contrôle (U + 0000 à U + 001F). " Puisqu'un saut de ligne est un caractère de contrôle, il doit être échappé.
daniel kullmann
1
Selon www.json.org, JSON accepte la séquence de contrôle "\ n" dans les chaînes - et si vous essayez JSON.parse (['"a \\ na"']) [1] .charCodeAt (); qui affichera 10 - qui était "Linefeed" la dernière fois que j'ai vérifié. --- BTW: Arrête de crier!
BlaM
+ 1. J'avais du mal à comprendre l'encodage JSON mais "deviendra une nouvelle ligne dans la source JSON, pas les données JSON", m'a expliqué clairement.
amucunguzi
44

Vous aurez besoin d'une fonction qui remplace \nau \\ncas où ce datan'est pas un littéral de chaîne.

function jsonEscape(str)  {
    return str.replace(/\n/g, "\\\\n").replace(/\r/g, "\\\\r").replace(/\t/g, "\\\\t");
}

var data = '{"count" : 1, "stack" : "sometext\n\n"}';
var dataObj = JSON.parse(jsonEscape(data));

Le résultat dataObjsera

Object {count: 1, stack: "sometext\n\n"}
manish_s
la source
3
vous devez échapper à vos personnages d'échappement (c.-à-d. .replace("\\n", "\\\\n")) et je suggérerais également d'utiliser l'expression régulière pour permettre de remplacer plusieurs instances (c.-à-d. .replace(/\n/g, "\\\\n"))
musefan
2
pourquoi avez-vous besoin d'échapper à des personnages évadés? Je veux dire quelque chose comme .replace("\n", "\\n")devrait bien faire le travail !! Par exemple, var test = [{"description":"Some description about the product. This can be multi-line text."}]; console.log(JSON.parse(test.replace(/\n/g, "\\n")));affichera parfaitement l'objet sur la console du navigateur comme[{"description":"Some description about the product.\nThis can be multi-line text."}]
Fr0zenFyr
BTW, dans le commentaire ci-dessus, la chaîne JSON d'origine a une nouvelle ligne, qui est supprimée par le formateur de commentaires de stackoverflow. Vous pouvez voir que la sortie finale après remplacement doit insérer un caractère de nouvelle ligne \ndans la valeur.
Fr0zenFyr
1
-1 Cette réponse construit d'abord une chaîne de JSON invalide (puisque la nouvelle ligne est un caractère de contrôle), puis essaie de le corriger avec une série de remplacements incomplets (il y a plus de 3 caractères de contrôle). Pour couronner le tout, il parvient également à utiliser la evalfonction. 17 votes positifs ???
Phil
1
Qu'en est-il des guillemets qui doivent également être échappés?
stand alone
8

Selon la spécification, http://www.ecma-international.org/publications/files/ECMA-ST/ECMA-404.pdf :

Une chaîne est une séquence de points de code Unicode entourée de guillemets ( U+0022). Tous les caractères peuvent être placés entre guillemets, à l'exception des caractères devant être échappés: guillemet ( U+0022), solidus inverse ( U+005C) et les caractères de contrôle U+0000à U+001F. Il existe des représentations de séquence d'échappement à deux caractères de certains caractères.

Vous ne pouvez donc pas passer 0x0Aou 0x0Ccoder directement. C'est interdit! La spécification suggère d'utiliser des séquences d'échappement pour certains codes bien définis de U+0000à U+001F:

  • \f représente le caractère de flux de formulaire (U+000C ).
  • \nreprésente le caractère de saut de ligne ( U+000A).

Comme la plupart des langages de programmation utilisent \pour les guillemets, vous devez échapper à la syntaxe d'échappement (double-échappement - une fois pour le langage / la plateforme, une fois pour JSON lui-même):

jsonStr = "{ \"name\": \"Multi\\nline.\" }";
gavenkoa
la source
3

Vous pouvez simplement échapper votre chaîne sur le serveur lors de l'écriture de la valeur du champ JSON et l'échapper lors de la récupération de la valeur dans le navigateur client, par exemple.

L'implémentation JavaScript de tous les principaux navigateurs a la commande unescape.

Exemple:

Sur le serveur:

response.write "{""field1"":""" & escape(RS_Temp("textField")) & """}"

Dans le navigateur:

document.getElementById("text1").value = unescape(jsonObject.field1)
Victor_Magalhaes
la source
2

Vous voudrez peut-être examiner cette fonction C # pour échapper à la chaîne:

http://www.aspcode.net/C-encode-a-string-for-JSON-JavaScript.aspx

public static string Enquote(string s)  
{ 
    if (s == null || s.Length == 0)  
    { 
        return "\"\""; 
    } 
    char         c; 
    int          i; 
    int          len = s.Length; 
    StringBuilder sb = new StringBuilder(len + 4); 
    string       t; 

    sb.Append('"'); 
    for (i = 0; i < len; i += 1)  
    { 
        c = s[i]; 
        if ((c == '\\') || (c == '"') || (c == '>')) 
        { 
            sb.Append('\\'); 
            sb.Append(c); 
        } 
        else if (c == '\b') 
            sb.Append("\\b"); 
        else if (c == '\t') 
            sb.Append("\\t"); 
        else if (c == '\n') 
            sb.Append("\\n"); 
        else if (c == '\f') 
            sb.Append("\\f"); 
        else if (c == '\r') 
            sb.Append("\\r"); 
        else 
        { 
            if (c < ' ')  
            { 
                //t = "000" + Integer.toHexString(c); 
                string t = new string(c,1); 
                t = "000" + int.Parse(tmp,System.Globalization.NumberStyles.HexNumber); 
                sb.Append("\\u" + t.Substring(t.Length - 4)); 
            }  
            else  
            { 
                sb.Append(c); 
            } 
        } 
    } 
    sb.Append('"'); 
    return sb.ToString(); 
} 
Ron
la source
3
Pourquoi cela s'échappe-t-il >?
rien n'est nécessaire le
0

J'ai utilisé cette fonction pour supprimer la nouvelle ligne ou d'autres caractères dans les données pour analyser les données JSON:

function normalize_str($str) {

    $invalid = array(
        'Š'=>'S', 'š'=>'s',  'Đ'=>'Dj', 'đ'=>'dj', 'Ž'=>'Z', 'ž'=>'z',
        'Č'=>'C', 'č'=>'c',  'Ć'=>'C',  'ć'=>'c',  'À'=>'A', 'Á'=>'A', 'Â'=>'A', 'Ã'=>'A',
        'Ä'=>'A', 'Å'=>'A',  'Æ'=>'A',  'Ç'=>'C',  'È'=>'E', 'É'=>'E', 'Ê'=>'E', 'Ë'=>'E',
        'Ì'=>'I', 'Í'=>'I',  'Î'=>'I',  'Ï'=>'I',  'Ñ'=>'N', 'Ò'=>'O', 'Ó'=>'O', 'Ô'=>'O',
        'Õ'=>'O', 'Ö'=>'O',  'Ø'=>'O',  'Ù'=>'U',  'Ú'=>'U', 'Û'=>'U', 'Ü'=>'U', 'Ý'=>'Y',
        'Þ'=>'B', 'ß'=>'Ss', 'à'=>'a',  'á'=>'a',  'â'=>'a', 'ã'=>'a', 'ä'=>'a', 'å'=>'a',
        'æ'=>'a', 'ç'=>'c',  'è'=>'e',  'é'=>'e',  'ê'=>'e', 'ë'=>'e', 'ì'=>'i', 'í'=>'i',
        'î'=>'i', 'ï'=>'i',  'ð'=>'o',  'ñ'=>'n',  'ò'=>'o', 'ó'=>'o', 'ô'=>'o', 'õ'=>'o',
        'ö'=>'o', 'ø'=>'o',  'ù'=>'u',  'ú'=>'u',  'û'=>'u', 'ý'=>'y', 'ý'=>'y', 'þ'=>'b',
        'ÿ'=>'y', 'Ŕ'=>'R',  'ŕ'=>'r',
        "`" => "'", "´" => "'",  '"' => ',',  '`' => "'",
        '´' => "'", '"' => '\"', '"' => "\"", '´' => "'",
        "&acirc;€™" => "'",
        "{" => "",
        "~" => "",  "–" => "-",  "'" => "'",  "     " => " ");

    $str = str_replace(array_keys($invalid), array_values($invalid), $str);

    $remove = array("\n", "\r\n", "\r");
    $str = str_replace($remove, "\\n", trim($str));

    //$str = htmlentities($str, ENT_QUOTES);

    return htmlspecialchars($str);
}

echo normalize_str($lst['address']);
ShivarajRH
la source
9
Dans la plupart des langues, vous avez de meilleurs moyens de supprimer les accents des chaînes unicode que de noter votre propre fonction de mappage. Voir cette question pour un exemple en python: stackoverflow.com/questions/517923/…
MiniQuark
ya nous avons plusieurs façons de contrôler les caractères spéciaux dans les langages diff.
ShivarajRH
2
C'est vraiment dommage de les déshabiller en général. Mieux les coder comme référence de caractères numériques XML, puis les décoder à la fin de la réception.
Annarfych
0

JSON.stringify

JSON.stringify(`{ 
  a:"a"
}`)

convertirait la chaîne ci-dessus en

"{ \n      a:\"a\"\n    }"

comme mentionné ici

json stringify

Cette fonction ajoute des guillemets doubles au début et à la fin de la chaîne d'entrée et échappe les caractères JSON spéciaux. En particulier, une nouvelle ligne est remplacée par le caractère \ n, un onglet est remplacé par le caractère \ t, une barre oblique inverse est remplacée par deux barres obliques inversées \ et une barre oblique inverse est placée avant chaque guillemet.

Mz A
la source
4
Il s'agit d'une réponse codée uniquement à une question de onze ans avec huit autres réponses existantes. Il est utile d'expliquer le code, mais aussi d'expliquer quel nouvel aspect de la question votre réponse aborde, et si le passage du temps et la sortie de nouvelles versions impactent votre réponse.
Jason Aller
-1

J'ai rencontré ce problème lors de la création d'une classe en PHP 4 pour émuler json_encode (disponible en PHP 5). Voici ce que j'ai trouvé:

class jsonResponse {
    var $response;

    function jsonResponse() {
        $this->response = array('isOK'=>'KO', 'msg'=>'Undefined');
    }

    function set($isOK, $msg) {
        $this->response['isOK'] = ($isOK) ? 'OK' : 'KO';
        $this->response['msg'] = htmlentities($msg);
    }

    function setData($data=null) {
        if(!is_null($data))
            $this->response['data'] = $data;
        elseif(isset($this->response['data']))
            unset($this->response['data']);
    }

    function send() {
        header('Content-type: application/json');
        echo '{"isOK":"' . $this->response['isOK'] . '","msg":' . $this->parseString($this->response['msg']);
        if(isset($this->response['data']))
            echo ',"data":' . $this->parseData($this->response['data']);
        echo '}';
    }

    function parseData($data) {
        if(is_array($data)) {
            $parsed = array();
            foreach ($data as $key=>$value)
                array_push($parsed, $this->parseString($key) . ':' . $this->parseData($value));
            return '{' . implode(',', $parsed) . '}';
        }
        else
            return $this->parseString($data);
    }

    function parseString($string) {
            $string = str_replace("\\", "\\\\", $string);
            $string = str_replace('/', "\\/", $string);
            $string = str_replace('"', "\\".'"', $string);
            $string = str_replace("\b", "\\b", $string);
            $string = str_replace("\t", "\\t", $string);
            $string = str_replace("\n", "\\n", $string);
            $string = str_replace("\f", "\\f", $string);
            $string = str_replace("\r", "\\r", $string);
            $string = str_replace("\u", "\\u", $string);
            return '"'.$string.'"';
    }
}

J'ai suivi les règles mentionnées ici . Je n'ai utilisé que ce dont j'avais besoin, mais je pense que vous pouvez l'adapter à vos besoins dans la langue que vous utilisez. Le problème dans mon cas ne concernait pas les nouvelles lignes comme je le pensais à l'origine, mais le / ne pas être échappé. J'espère que cela empêchera quelqu'un d'autre du petit mal de tête que j'avais de comprendre ce que j'ai fait de mal.

GabrielP
la source
2
Les 6 raccourcis pour les caractères de contrôle spécifiés sur json.org ne sont pas une liste exhaustive de tous les caractères de contrôle. Par conséquent, cette fonction pourrait générer un JSON non valide.
Phil
-5

Comme je vous comprends la question, il est pas l' analyse syntaxique JSON parce que vous pouvez copier-coller votre JSON dans votre code directement - donc si tel est le cas , alors il suffit de copier votre JSON directement à la dataObjvariable sans l' emballer avec des guillemets simples (conseil: eval==evil)

var dataObj = {"count" : 1, "stack" : "sometext\n\n"};

console.log(dataObj);

Kamil Kiełczewski
la source