jQuery.parseJSON lança erro "JSON inválido" devido a aspas simples escapadas em JSON

202

Estou fazendo solicitações ao meu servidor usando jQuery.post()e meu servidor está retornando objetos JSON (como { "var": "value", ... }). No entanto, se algum dos valores contiver uma única citação (como escapou corretamente \'), o jQuery falhará ao analisar uma sequência JSON válida, caso contrário. Aqui está um exemplo do que quero dizer ( feito no console do Chrome ):

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

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

Isso é normal? Não há como passar corretamente uma única cotação via JSON?

Felix
fonte

Respostas:

325

De acordo com o diagrama da máquina de estado no site JSON , apenas caracteres de aspas duplas escapados são permitidos, não aspas simples. Os caracteres de aspas simples não precisam ser escapados:

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


Atualização - Mais informações para aqueles que estão interessados:


Douglas Crockford não diz especificamente por que a especificação JSON não permite aspas simples escapadas dentro de strings. No entanto, durante sua discussão sobre JSON no Apêndice E do JavaScript: The Good Parts , ele escreve:

Os objetivos de design do JSON eram mínimos, portáteis, textuais e um subconjunto de JavaScript. Quanto menos precisamos concordar para interoperar, mais facilmente podemos interoperar.

Portanto, talvez ele tenha decidido permitir apenas que cadeias sejam definidas usando aspas duplas, pois essa é uma regra a menos com a qual todas as implementações JSON devem concordar. Como resultado, é impossível que um caractere de aspas simples dentro de uma string termine acidentalmente a string, porque, por definição, uma string só pode ser terminada por um caractere de aspas duplas. Portanto, não há necessidade de permitir o escape de um caractere de aspas simples na especificação formal.


Indo um pouco mais profundo, do Crockford org.json implementação de JSON para Java é mais admissível e faz permitir caracteres de aspas simples:

Os textos produzidos pelos métodos toString estão em conformidade estrita com as regras de sintaxe JSON. Os construtores são mais tolerantes nos textos que aceitarão:

...

  • As strings podem ser citadas com '(aspas simples).

Isso é confirmado pelo código-fonte JSONTokener . O nextStringmétodo aceita caracteres de aspas simples escapados e os trata como caracteres de aspas duplas:

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

No topo do método, há um comentário informativo:

O formato JSON formal não permite seqüências de caracteres entre aspas simples, mas uma implementação pode aceitá-las.

Portanto, algumas implementações aceitarão aspas simples - mas você não deve confiar nisso. Muitas implementações populares são bastante restritivas nesse sentido e rejeitarão o JSON que contém seqüências de caracteres entre aspas simples e / ou aspas simples escapadas.


Finalmente, para vincular isso à pergunta original, jQuery.parseJSONprimeiro tente usar o analisador JSON nativo do navegador ou uma biblioteca carregada, como json2.js, quando aplicável (que em uma nota lateral é a biblioteca na qual a lógica do jQuery se baseia se JSONnão estiver definida) . Portanto, o jQuery pode ser tão permissivo quanto a implementação subjacente:

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

Tanto quanto sei, essas implementações seguem apenas a especificação JSON oficial e não aceitam aspas simples, portanto, o jQuery também não.

Justin Ethier
fonte
4
UPDATE :: JQuery é muito restritivo ao colar JSON. Se você tentar o alerta ($. ParseJSON ("[\" Ciao \\ '\ "]"))); ele não funciona por causa do que Justin informou
daitangio
2
" Implementação org.json do Crockford de JSON para Java é mais admissível e não permite cotação caracteres simples " # isso é apenas boa prática: princípio robustez
Duncan Jones
1
@DuncanJones - Este artigo pode fornecer uma visão sobre por que nenhum dos navegadores parecem seguir esse princípio em relação a JSON: joelonsoftware.com/items/2008/03/17.html
Justin Ethier
1
@JustinEthier como apontado por esta resposta stackoverflow.com/a/25491642/759452 , o JSON especificação tools.ietf.org/html/rfc7159 diz Any character may be escaped, isso pode explicar por que alguns implementação permitiria aspas simples para ser escapado.
Adrien Seja
1
@AdrienBe - Interessante ... mas eles significam que qualquer caractere pode ser escapado se consistir em 4 dígitos hexadecimais? De acordo com o diagrama de estados acima e o da seção 7 da RFC, \'ainda não é permitido o escape de uma única citação conforme escrita . Seria bom se o RFC fosse mais explícito nesse ponto.
23614 Justin Ethier
15

Se você precisa de uma aspa simples dentro de uma string, uma vez que \' é indefinido pela especificação, o uso \u0027 ver http://www.utf8-chartable.de/ para todos eles

edit: desculpe meu uso indevido da palavra backticks nos comentários. Eu quis dizer barra invertida. Meu argumento aqui é que, no caso de você ter aninhado cadeias dentro de outras cadeias, acho que pode ser mais útil e legível usar unicode em vez de muitas barras invertidas para escapar de uma única citação. Se você não estiver aninhado, no entanto, é realmente mais fácil colocar uma citação simples e antiga.

slf
fonte
29
Não. Basta usar uma aspas simples.
Jeff Kaufman
Às vezes, é muito mais fácil usar unicode do que toneladas de back-ticks. Particularmente quando dentro de back-ticks alternados.
slf
3
Por que você precisaria de backticks? Se você tem uma string como "foo 'bar'", basta deixar as aspas simples sem escape.
21412 Jeff Kaufman
3
Exatamente o que eu estava procurando. Estou tentando escrever uma string json em uma página como uma string js var e coloque-a entre aspas simples e era finalizada mais cedo sempre que um valor de propriedade continha uma única citação. Agora eu apenas faço um json.Replace ("'", "\ u0027") no código antes de escrevê-lo na página.
Zack
@ Zack Você não deve incluir JSON entre aspas. Se você precisar de uma sequência fora de uma sequência JSON existente, apenas a especifique novamente. em PHP, é var jsonEncodedAsString = <?= json_encode(myEncodedJson) ?>aí que myEncodedJsonestá o resultado de um anterior. json_encodeIsso resolverá o escape de sua aspas simples, na verdade, apenas produzirá algo em uma grande string envolta em aspas duplas, para que aspas simples não sejam escapadas, mas aspas duplas vai.
Juan Mendes
5

Entendo onde está o problema e, quando olho as especificações, fica claro que aspas simples sem escape devem ser analisadas corretamente.

Estou usando a função jQuery.parseJSON do jquery para analisar a string JSON, mas ainda obtendo o erro de análise quando há uma única citação nos dados preparados com json_encode.

Poderia ser um erro na minha implementação que se parece com isso (PHP - lado do 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) . "]";

A etapa final é que eu armazene a string codificada em JSON em uma variável JS:

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

Se eu usar "" em vez de '', ele ainda gera um erro.

SOLUÇÃO:

A única coisa que funcionou para mim foi usar a máscara de bit JSON_HEX_APOS para converter aspas simples assim:

json_encode($tmp, JSON_HEX_APOS);

Existe outra maneira de resolver esse problema? Meu código está errado ou está mal escrito?

obrigado

Erik Čerpnjak
fonte
'<? = $ marcador; ?> 'isso não é válido json. As aspas circundantes são "consumidas" pelo intérprete javascript, deixando uma string que começa com um <... o que você realmente queria tentar era um destes: jQuery.parseJSON ('"<? = $ Marker;?>" '); jQuery.parseJSON ("\" <? = $ marcador;?> \ ""); De acordo com a especificação, json strings deve usar aspas duplas, mas o javascript não se importa, portanto, você tem uma string javascript de aspas simples ou aspas duplas, mas se você usar a última, terá que escapar de todas as usos de aspas duplas dentro da string.
precisa saber é o seguinte
3

Quando você está enviando uma aspas simples em uma consulta

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

Quando você obtém o valor, incluindo uma única citação

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

Se você deseja pesquisar / inserir o valor que inclui uma aspas simples em uma consulta xxx=Replace(empid,"'","''")

BehranG BinA
fonte
1
Mas não há necessidade de escapar de uma única citação ao passá-la como parte de uma string JSON ...
Justin Ethier 15/03
2

Ocorreu um problema semelhante usando o CakePHP para gerar um bloco de script JavaScript usando o nativo do PHP json_encode. $contractorCompaniescontém valores que possuem aspas simples e, como explicado acima e o esperado json_encode($contractorCompanies), não os escapam porque seu JSON válido.

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

Adicionando addlashes () ao redor da string codificada em JSON, você escapa as aspas, permitindo que o Cake / PHP faça eco do javascript correto no navegador. Os erros do JS desaparecem.

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

Eu estava tentando salvar um objeto JSON de uma solicitação XHR em um atributo data- * HTML5. Eu tentei muitas das soluções acima sem sucesso.

O que finalmente acabei fazendo foi substituir a aspas simples 'pelo código &#39;usando um regex após o método stringify () chamar da seguinte maneira:

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
fonte
0

Interessante. Como você está gerando seu JSON no servidor final? Você está usando uma função de biblioteca (como json_encodeno PHP) ou está construindo a string JSON manualmente?

A única coisa que chama minha atenção é o apóstrofo da fuga ( \'). Como você está usando aspas duplas, como realmente deveria, não há necessidade de escapar de aspas simples. Não consigo verificar se essa é realmente a causa do seu erro do jQuery, pois ainda não atualizei para a versão 1.4.1.

Aistina
fonte
Eu uso uma biblioteca em PHP para gerar o objeto JSON - meio que perdi anotá-lo. Obrigado por apontar isso.
Erik Čerpnjak 27/01