Melhor maneira de armazenar JSON em um atributo HTML?

112

Preciso colocar um objeto JSON em um atributo em um elemento HTML.

  1. O HTML não precisa ser validado.

    Respondido por Quentin: Armazene o JSON em um data-*atributo , que é HTML5 válido.

  2. O objeto JSON pode ter qualquer tamanho - ou seja, enorme

    Respondido por Maiku Mori: O limite para um atributo HTML é potencialmente de 65536 caracteres .

  3. E se o JSON contiver caracteres especiais? por exemplo {foo: '<"bar/>'}

    Respondido por Quentin: Codifique a string JSON antes de colocá-la no atributo, conforme as convenções usuais. Para PHP, use a função . htmlentities()


EDIT - Exemplo de solução usando PHP e jQuery

Escrevendo o JSON no atributo HTML:

<?php
    $data = array(
        '1' => 'test',
        'foo' => '<"bar/>'
    );
    $json = json_encode($data);
?>

<a href="#" data-json="<?php echo htmlentities($json, ENT_QUOTES, 'UTF-8'); ?>">CLICK ME</a>

Recuperando o JSON usando jQuery:

$('a').click(function() {

    // Read the contents of the attribute (returns a string)
    var data = $(this).data('json');

    // Parse the string back into a proper JSON object
    var json = $.parseJSON($(this).data('json'));

    // Object now available
    console.log(json.foo);

});
BadHorsie
fonte
Você provavelmente deve explicar o porquê e pedir uma solução diferente, já que tenho certeza de que não é a melhor. Você provavelmente pode usar atributos data-something, mas não tenho certeza se eles podem conter uma quantidade "enorme" de texto. Quanto aos caracteres especiais, você pode apenas codificar (escape () e unescape ()) o texto.
Maiku Mori
Sim, o limite é 65536 caracteres ( stackoverflow.com/questions/2752457/… )
Maiku Mori
1
A propósito, se o seu atributo for nomeado, data-jsonvocê deve usar $(this).data('json')o jQuery para ajudá-lo nessa parte.
Ciantic
Apenas uma observação: nomear o sufixo de dados para json não é necessário. Se você colocar json válido em qualquer data-custom_attribute, ele funcionará bem com jQuery.
Garet Claborn
corrija a sequência de colchetes)}; =>});
MotsManish de

Respostas:

41

O HTML não precisa ser validado.

Por que não? A validação é um controle de qualidade realmente fácil que detecta muitos erros. Use um data-*atributo HTML 5 .

O objeto JSON pode ter qualquer tamanho (ou seja, enorme).

Eu não vi nenhuma documentação sobre os limites do navegador para tamanhos de atributos.

Se você os encontrar, armazene os dados em a <script>. Defina um objeto e mapeie os elementos idpara nomes de propriedades nesse objeto.

E se o JSON contiver caracteres especiais? (por exemplo, {teste: '<"myString />'})

Basta seguir as regras normais para incluir dados não confiáveis ​​em valores de atributos. Use &amp;e &quot;(se você estiver colocando o valor do atributo entre aspas duplas) ou &#x27;(se estiver colocando o valor do atributo entre aspas simples).

Observe, entretanto, que isso não é JSON (que requer que os nomes das propriedades sejam strings e que as strings sejam delimitadas apenas com aspas duplas).

Quentin
fonte
5
Então você está dizendo que devo fazer htmlentities($json)antes de colocá-lo no atributo HTML? E então como faço para decodificar isso quando quero ler no jQuery? E então como faço para escrever de volta usando jQuery da mesma forma que era em PHP?
BadHorsie
6
Então você está dizendo que eu devo fazer html_encode ($ json) antes de colocá-lo no atributo HTML? - se você estiver usando PHP, isso funcionaria. E então como faço para decodificar isso quando quero ler no jQuery? - Decodificar do atributo? O navegador fará isso ao analisar o HTML em um DOM. E então como faço para escrever de volta usando jQuery da mesma forma que era em PHP? - Você está definindo atributos de nós DOM, não gerando HTML bruto, o navegador cuidará disso.
Quentin,
1
No momento, tenho um problema em que meu navegador não está decodificando no Google Chrome e, quando vou analisar JSON, todas as entidades HTML estão lá e falham.
Bradley Weston
Se você colocá-lo em uma tag de script, terá que fazer um escape diferente devido ao tratamento especial das tags de script. por exemplo, um valor com </script> nele encerraria a tag do script.
Dobes Vandermeer
16

Dependendo de onde você o colocar,

  • Conforme <div>solicitado, você precisa garantir que o JSON não contenha especiais HTML que possam iniciar uma tag, comentário HTML, tipo de documento incorporado, etc. Você precisa pelo menos escapar <e& forma que o caractere original não aparecem na sequência de escape.
  • Em <script>elementos, você precisa garantir que o JSON não contenha uma tag de finalização </script>ou limite de texto de escape: <!--ou -->.
  • Em manipuladores de eventos, você precisa garantir que o JSON preserva seu significado, mesmo se tiver coisas que se pareçam com entidades HTML e não quebre os limites do atributo ( "ou ').

Para os dois primeiros casos (e para analisadores JSON antigos), você deve codificar U + 2028 e U + 2029, pois esses são caracteres de nova linha em JavaScript, embora sejam permitidos em strings não codificadas em JSON.

Para correção, você precisa escapar \e aspas JSON e nunca é uma má ideia sempre codificar NUL.

Se o HTML pode ser servido sem uma codificação de conteúdo, você deve codificar +para evitar ataques UTF-7 .

Em qualquer caso, a seguinte tabela de escape funcionará:

  • NUL -> \u0000
  • CR -> \nou\u000a
  • LF -> \rou\u000d
  • " -> \u0022
  • & -> \u0026
  • ' -> \u0027
  • + -> \u002b
  • /-> \/ou\u002f
  • < -> \u003c
  • > -> \u003e
  • \-> \\ou\u005c
  • U + 2028 ->\u2028
  • U + 2029 -> \u2029

Portanto, o valor da string JSON para o texto Hello, <World>!com uma nova linha no final seria "Hello, \u003cWorld\u003e!\r\n".

Mike Samuel
fonte
1
return (input.replace(/([\s"'& + \ / \\ <> \ u2028 \ u2029 \ u0000]) / g, (combinar, p1) => {retornar \\u${p1.codePointAt(0).toString(16).padStart(4, 0)}; })); `
Denis Giffeler
14

Outra maneira de fazer isso é colocar os dados json dentro da <script>tag, mas não com type="text/javascript", mas com type="text/bootstrap"ou type="text/json"tipo, para evitar a execução de javascript.

Então, em algum lugar do seu programa, você pode solicitá-lo desta forma:

function getData(key) {
  try {
    return JSON.parse($('script[type="text/json"]#' + key).text());
  } catch (err) { // if we have not valid json or dont have it
    return null;
  } 
}

No lado do servidor, você pode fazer algo assim (este exemplo com php e twig ):

<script id="my_model" type="text/json">
  {{ my_model|json_encode()|raw }}
</script>
Sergey Kamardin
fonte
1
Para JSON, use o tipo de script "application / json". Também é bom ter o nível superior como um objeto a longo prazo.
OIS
1
Isso não responde diretamente à pergunta do op, mas ainda assim foi muito útil para mim. Os dados que estou armazenando se aplicam à página como um todo e não a qualquer elemento específico, portanto, um atributo de elemento realmente não funciona (a menos que eu o coloque como no elemento de corpo, o que me parece meio chato, especialmente porque meu os dados podem ser grandes). Armazená-lo em um <script type="application/json" id="MyData"></script>funciona perfeitamente. Em seguida, usando ActiveWAFL / DblEj, posso procurá-lo com: document.GetData("#MyData").
Evan de la Cruz
2
Esta resposta contém informações falsas / perigosas! Alterar o tipo de script não modifica a detecção de tags </script>. Experimente:<script type="application/json" id="MyData"> "abc</script><script>alert()</script>" </script>
hyperknot
12

Outra opção é codificar em base64 a string JSON e, se precisar usá-la em seu javascript, decodifique-a com a atob()função.

var data = JSON.parse(atob(base64EncodedJSON));
Pavel Petrov
fonte
Obrigado por atob. Nunca me dei conta disso!
Ifedi Okonkwo
3
Cuidado - não funciona se JSON contiver caracteres não latinos.
Realexer
5

Você pode usar knockoutjs,

<p>First name: <strong data-bind="text: firstName" >todo</strong></p>
<p>Last name: <strong data-bind="text: lastName">todo</strong></p>

knockout.js

// This is a simple *viewmodel* - JavaScript that defines the data and behavior of your UI
function AppViewModel() {
    this.firstName = "Jayson";
    this.lastName = "Monterroso";
}

// Activates knockout.js
ko.applyBindings(new AppViewModel());

Resultado

Primeiro nome: Jayson Sobrenome: Monterroso

Verifique isto: http://learn.knockoutjs.com/

JayM
fonte
2

Nada extravagante aqui. No PHP, dê uma olhada na string JSON htmlspecialcharspara garantir que nenhum caractere especial possa ser interpretado como HTML. Do Javascript, nenhum escape é necessário; basta definir o atributo e pronto.

Matchu
fonte
2

Para objetos JSON simples, o código abaixo funcionaria.

Codificar:

var jsonObject = { numCells: 5, cellWidth: 1242 };
var attributeString = escape(JSON.stringify(jsonObject));

Decodificar:

var jsonString = unescape(attributeString);
var jsonObject = JSON.parse(jsonString);
Crashalot
fonte
1

O que você pode fazer é usar cdata em torno de seu (s) elemento (s) como este

<![CDATA[  <div class='log' mydata='${aL.logData}'>${aL.logMessage}</div>     ]]>  

onde mydata é uma string json bruta. Espero que isso ajude você e outras pessoas.

Faiyet
fonte
Como (pode) essa solução funcionar? E se eu quiser armazenar algo como ">em meus dados?
Martin Pecka,
1
E se a string contiver "]]>"
Dobes Vandermeer
1

Outro pensamento que pode ser usado é armazenar os dados JSON como uma string base64 no atributo e, em seguida, usar window.atobou window.btoapara restaurá-los para dados JSON utilizáveis.

<?php
$json = array("data"=>"Some json data thing");
echo "<div data-json=\"".base64_encode(json_encode($json))."\"></div>";
?>
Matthew D Auld
fonte