Como lidar com novas linhas em JSON?

289

Eu gerei algum JSON e estou tentando inseri-lo em um objeto em JavaScript. Eu continuo recebendo erros. Aqui está o que eu tenho:

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

Isso me dá um erro:

unterminated string literal

Com JSON.parse(data), vejo mensagens de erro semelhantes: " Unexpected token ↵" no Chrome e "unterminated string literal " no Firefox e IE.

Quando tiro o \ndepois, sometexto erro desaparece nos dois casos. Parece que não consigo descobrir por que os \nerros evale as JSON.parsefalhas.

Urso polar
fonte
19
Tente usar um analisador json real em vez de eval.
Eric

Respostas:

368

Eu acho que é isso que você quer:

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

(Você precisa escapar do "\" na sua cadeia de caracteres (transformando-o em um duplo - "\"), caso contrário, ela se tornará uma nova linha na fonte JSON, não nos dados JSON.)

BlaM
fonte
101
É claro que isso está correto, mas eu gostaria de acrescentar o motivo de ter que fazer isso: a especificação JSON em ietf.org/rfc/rfc4627.txt contém esta frase na seção 2.5: "Todos os caracteres Unicode podem ser colocados no diretório aspas, exceto os caracteres que devem ser escapados: aspas, solidus reverso e os caracteres de controle (U + 0000 a U + 001F). " Como uma nova linha é um caractere de controle, ela deve ser escapada.
Daniel kullmann #
1
De acordo com www.json.org, o JSON aceita a sequência de controle "\ n" em strings - e se você tentar JSON.parse (['"a \\ na"']) [1] .charCodeAt (); que mostrará 10 - que foi "Linefeed" da última vez que verifiquei. --- BTW: Pare de gritar!
BlaM 11/11/2015
+ 1. Eu estava tendo problemas para entender a codificação JSON, mas "se tornará uma nova linha na fonte JSON, não nos dados JSON" deixou claro para mim.
amucunguzi
44

Você precisará ter uma função que substitua \npara \\n, caso datanão seja uma string literal.

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

O resultado dataObjserá

Object {count: 1, stack: "sometext\n\n"}
manish_s
fonte
3
você precisa escapar de seus caracteres de escape (ie .replace("\\n", "\\\\n")) e gostaria também sugerem usando regex para permitir a substituição de várias instâncias (ie .replace(/\n/g, "\\\\n"))
musefan
2
por que você precisa escapar dos caracteres de escape? Quero dizer algo como .replace("\n", "\\n")deve fazer o trabalho bem !! Por exemplo, var test = [{"description":"Some description about the product. This can be multi-line text."}]; console.log(JSON.parse(test.replace(/\n/g, "\\n")));produzirá o objeto perfeitamente bem no console do navegador como[{"description":"Some description about the product.\nThis can be multi-line text."}]
Fr0zenFyr 27/11/2015
BTW, no comentário acima, a string JSON original possui uma nova linha, que é removida pelo formatador de comentários do stackoverflow. Você pode ver que a saída final após a substituição deve inserir um caractere de nova linha \nno valor.
Fr0zenFyr
1
-1 Essa resposta primeiro constrói uma sequência de JSON inválida (como newline é um caractere de controle) e tenta corrigi-la com uma série de substituições incompletas (há mais de 3 caracteres de controle). Além disso, ele também consegue usar a evalfunção. 17 votos positivos ???
24417 Phil
1
E as aspas que precisam ser escapadas também?
stand alone
8

De acordo com a especificação, http://www.ecma-international.org/publications/files/ECMA-ST/ECMA-404.pdf :

Uma sequência é uma sequência de pontos de código Unicode agrupados por aspas ( U+0022). Todos os caracteres podem ser colocados entre aspas, exceto os que devem ser escapados: aspas ( U+0022), solidus reverso ( U+005C) e os caracteres de controle U+0000para U+001F. Existem representações de seqüência de escape de dois caracteres de alguns caracteres.

Portanto, você não pode passar 0x0Aou 0x0Ccódigos diretamente. É proibido! A especificação sugere o uso de seqüências de escape para alguns códigos bem definidos de U+0000a U+001F:

  • \frepresenta o caractere de feed de formulário ( U+000C).
  • \nrepresenta o caractere de avanço de linha ( U+000A).

Como a maioria das linguagens de programação usa \para citação, você deve escapar da sintaxe de escape (escape duplo - uma vez para idioma / plataforma, uma vez para o próprio JSON):

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

Você pode simplesmente escapar sua cadeia de caracteres no servidor ao escrever o valor do campo JSON e desescapá-lo ao recuperar o valor no navegador do cliente, por exemplo.

A implementação JavaScript de todos os principais navegadores possui o comando unescape.

Exemplo:

No servidor:

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

No navegador:

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

Você pode querer olhar para esta função C # para escapar da string:

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
fonte
3
Por que isso escapa >?
nothingisnecessary
0

Eu usei essa função para remover nova linha ou outros caracteres nos dados para analisar dados 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
fonte
9
Na maioria dos idiomas, você tem maneiras melhores de remover acentos de cadeias unicode do que escrever sua própria função de mapeamento. Veja esta pergunta para obter um exemplo em python: stackoverflow.com/questions/517923/…
MiniQuark
ya, temos muitas maneiras de controlar os caracteres especiais nos idiomas diff.
ShivarajRH
2
Isso é muito ruim para tirá-los em geral. É melhor codificá-los como referência de caracteres numéricos XML e decodificar no recebimento final.
Annarfych #
0

JSON.stringify

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

converteria a string acima em

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

como mencionado aqui

json stringify

Essa função adiciona aspas duplas no início e no final da sequência de entrada e escapa caracteres JSON especiais. Em particular, uma nova linha é substituída pelo caractere \ n, uma guia é substituída pelo caractere \ t, uma barra invertida é substituída por duas barras invertidas \ e uma barra invertida é colocada antes de cada aspas.

Mz A
fonte
4
Esta é uma resposta apenas de código a uma pergunta de onze anos com oito outras respostas existentes. É útil explicar o código e também para explicar o novo aspecto da pergunta que sua resposta aborda e se a passagem do tempo e o lançamento de novas versões afetam sua resposta.
Jason Aller
-1

Eu encontrei esse problema ao criar uma classe no PHP 4 para emular o json_encode (disponível no PHP 5). Aqui está o que eu vim com:

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.'"';
    }
}

Eu segui as regras mencionadas aqui . Eu usei apenas o que eu precisava, mas acho que você pode adaptá-lo às suas necessidades no idioma que está usando. O problema no meu caso não era sobre novas linhas como eu pensava originalmente, mas sobre o / não ser escapado. Espero que isso impeça outra pessoa da pequena dor de cabeça que tive para descobrir o que fiz de errado.

GabrielP
fonte
2
As 6 atalhos para caracteres de controle especificados em json.org não são uma lista exaustiva de todos os caracteres de controle. Como resultado, essa função pode gerar JSON inválido.
24417 Phil
-5

Como eu entendo que você pergunta, não se trata de analisar JSON porque você pode copiar e colar o seu JSON em seu código diretamente - por isso, se este for o caso, em seguida, basta copiar o seu JSON dirigir a dataObjvariável sem envolvê-lo com aspas simples (dica: eval==evil)

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

console.log(dataObj);

Kamil Kiełczewski
fonte