Il modo migliore per memorizzare JSON in un attributo HTML?


112

Ho bisogno di inserire un oggetto JSON in un attributo su un elemento HTML.

  1. L'HTML non deve essere convalidato.

    Risposta di Quentin: Memorizza il JSON in un data-*attributo , che è HTML5 valido.

  2. L'oggetto JSON potrebbe essere di qualsiasi dimensione, ovvero enorme

    Risposta di Maiku Mori: Il limite per un attributo HTML è potenzialmente di 65536 caratteri .

  3. Cosa succede se il JSON contiene caratteri speciali? per esempio {foo: '<"bar/>'}

    Risposta di Quentin: codifica la stringa JSON prima di inserirla nell'attributo, secondo le normali convenzioni. Per PHP, usa la funzione . htmlentities()


EDIT - Soluzione di esempio che utilizza PHP e jQuery

Scrittura del JSON nell'attributo 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>

Recupero del JSON utilizzando 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);

});

Probabilmente dovresti spiegare perché e chiedere una soluzione diversa poiché sono abbastanza sicuro che non sia la migliore. Puoi probabilmente usare attributi data-something ma non sono sicuro che possano contenere una quantità "enorme" di testo. Per quanto riguarda i caratteri speciali, puoi semplicemente codificare (escape () e unescape ()) il testo.
Maiku Mori

Sì, il limite è 65536 caratteri ( stackoverflow.com/questions/2752457/… )
Maiku Mori

1
A proposito, se il tuo attributo è chiamato data-jsondovresti usare $(this).data('json'), jQuery ti ha coperto da quella parte.
Ciantic

Solo una nota, non è necessario denominare il suffisso dei dati in json. Se metti un json valido in qualsiasi data-custom_attribute, funzionerà bene con jQuery.
Garet Claborn

si prega di correggere la sequenza delle parentesi graffe)}; =>});
MotsManish,

Risposte:


41

L'HTML non deve essere convalidato.

Perchè no? La convalida è davvero facile QA che cattura molti errori. Utilizza un data-*attributo HTML 5 .

L'oggetto JSON potrebbe essere di qualsiasi dimensione (ovvero enorme).

Non ho visto alcuna documentazione sui limiti del browser per le dimensioni degli attributi.

Se li incontri, archivia i dati in un file <script>. Definire un oggetto e mappare gli elementi idai nomi delle proprietà in quell'oggetto.

Cosa succede se il JSON contiene caratteri speciali? (ad es. {test: '<"myString />'})

Segui le normali regole per includere dati non attendibili nei valori degli attributi. Usa &amp;e &quot;(se stai racchiudendo il valore dell'attributo tra virgolette doppie) o &#x27;(se stai racchiudendo il valore dell'attributo tra virgolette singole).

Si noti, tuttavia, che questo non è JSON (che richiede che i nomi delle proprietà siano stringhe e le stringhe siano delimitate solo da virgolette doppie).


5
Quindi stai dicendo che dovrei farlo htmlentities($json)prima di inserirlo nell'attributo HTML? E poi come lo decodifico quando voglio leggerlo in jQuery? E poi come faccio a riscriverlo usando jQuery nello stesso modo in cui era in PHP?
BadHorsie

6
Quindi stai dicendo che dovrei fare html_encode ($ json) prima di inserirlo nell'attributo HTML? - se stai usando PHP, allora funzionerebbe. E poi come lo decodifico quando voglio leggerlo in jQuery? - Decodificare dall'attributo? Il browser lo farà quando analizza l'HTML in un DOM. E poi come faccio a riscriverlo usando jQuery nello stesso modo in cui era in PHP? - Stai impostando gli attributi dei nodi DOM, non generando HTML grezzo, il browser se ne occuperà.
Quentin

1
Al momento ho un problema in cui il mio browser non lo decodifica attualmente su Google Chrome e quando vado ad analizzare JSON tutte le entità HTML sono presenti e falliscono.
Bradley Weston

Se lo metti in un tag di script, devi eseguire l'escape in modo diverso a causa della gestione speciale dei tag di script. ad esempio un valore con </script> in esso terminerebbe il tag script.
Dobes Vandermeer

16

A seconda di dove lo metti,

  • In una <div>come lei ha chiesto, è necessario assicurarsi che il JSON non contiene offerte speciali HTML che potrebbe iniziare un tag, commento HTML, doctype incorporato, ecc Hai bisogno di fuggire, almeno <, e &in modo tale che il personaggio originale non lo fa appaiono nella sequenza di escape.
  • Negli <script>elementi è necessario assicurarsi che il JSON non contenga un tag di fine </script>o un limite di testo di escape: <!--o -->.
  • Nei gestori di eventi è necessario assicurarsi che JSON conservi il suo significato anche se ha cose che sembrano entità HTML e non infrange i limiti degli attributi ( "o ').

Per i primi due casi (e per i vecchi parser JSON) dovresti codificare U + 2028 e U + 2029 poiché si tratta di caratteri di nuova riga in JavaScript anche se sono consentiti nelle stringhe non codificate in JSON.

Per correttezza, è necessario eseguire l'escape \e le virgolette JSON e non è mai una cattiva idea codificare sempre NUL.

Se l'HTML può essere offerto senza una codifica del contenuto, dovresti codificare + per prevenire attacchi UTF-7 .

In ogni caso, funzionerà la seguente tabella di escape:

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

Quindi il valore della stringa JSON per il testo Hello, <World>!con una nuova riga alla fine sarebbe "Hello, \u003cWorld\u003e!\r\n".


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

14

Un altro modo per farlo è inserire i dati json all'interno del <script>tag, ma non con type="text/javascript", ma con type="text/bootstrap"o type="text/json", per evitare l'esecuzione di javascript.

Quindi, in qualche punto del tuo programma, puoi richiederlo in questo modo:

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

Sul lato server, puoi fare qualcosa del genere (questo esempio con php e twig ):

<script id="my_model" type="text/json">
  {{ my_model|json_encode()|raw }}
</script>

1
Per JSON utilizzare il tipo di script "application / json". Inoltre è bello avere il livello più alto come oggetto nel lungo periodo.
OIS

1
Questo non risponde direttamente alla domanda dell'operazione, ma è stato comunque molto utile per me. I dati che sto memorizzando si applicano alla pagina nel suo insieme e non a qualsiasi elemento specifico, quindi un attributo di elemento non funziona davvero (a meno che non lo metta come sull'elemento body, che mi sembra un po 'noioso soprattutto perché il mio i dati possono essere grandi). Conservarlo in un <script type="application/json" id="MyData"></script>funziona perfettamente. Quindi, utilizzando ActiveWAFL / DblEj, posso guardare in su con: document.GetData("#MyData").
Evan de la Cruz

2
Questa risposta contiene informazioni false / pericolose! La modifica del tipo di script non modifica il rilevamento dei tag </script>. Prova:<script type="application/json" id="MyData"> "abc</script><script>alert()</script>" </script>
hyperknot

12

Un'altra opzione è codificare in base64 la stringa JSON e se è necessario utilizzarla nel proprio javascript decodificarla con la atob()funzione.

var data = JSON.parse(atob(base64EncodedJSON));

Grazie per atob. Non ne sono mai stato consapevole!
Ifedi Okonkwo

3
Attenzione: non funziona se JSON contiene caratteri non latini.
Realexer

5

Puoi usare 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());

Produzione

Nome: Jayson Cognome: Monterroso

Controlla questo: http://learn.knockoutjs.com/


2

Niente di speciale qui. Da PHP, dai un'occhiata alla stringa JSON htmlspecialcharsper assicurarti che nessun carattere speciale possa essere interpretato come HTML. Da Javascript, non è necessario eseguire l'escape; imposta l'attributo e sei a posto.


2

Per semplici oggetti JSON, il codice seguente funzionerebbe.

Codificare:

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

Decodificare:

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

1

Quello che puoi fare è usare cdata attorno al tuo elemento / i in questo modo

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

dove mydata è una stringa json non elaborata. Spero che questo aiuti te e gli altri.


Come funziona (può) questa soluzione? E se volessi memorizzare qualcosa come ">in mydata?
Martin Pecka

1
E se la stringa contiene "]]>"
Dobes Vandermeer

1

Un altro pensiero che potrebbe essere utilizzato è memorizzare i dati JSON come stringa base64 nell'attributo e quindi utilizzare window.atobo window.btoaper ripristinarli su dati JSON utilizzabili.

<?php
$json = array("data"=>"Some json data thing");
echo "<div data-json=\"".base64_encode(json_encode($json))."\"></div>";
?>
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.