La meilleure façon de stocker JSON dans un attribut HTML?

112

J'ai besoin de mettre un objet JSON dans un attribut sur un élément HTML.

  1. Le HTML n'a pas à valider.

    Réponse de Quentin: Stockez le JSON dans un data-*attribut , qui est HTML5 valide.

  2. L'objet JSON peut être de n'importe quelle taille - c'est-à-dire énorme

    Réponse de Maiku Mori: La limite pour un attribut HTML est potentiellement de 65 536 caractères .

  3. Et si le JSON contient des caractères spéciaux? par exemple {foo: '<"bar/>'}

    Réponse de Quentin: Encodez la chaîne JSON avant de la mettre dans l'attribut, selon les conventions habituelles. Pour PHP, utilisez la fonction . htmlentities()


EDIT - Exemple de solution utilisant PHP et jQuery

Écriture du JSON dans l'attribut 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>

Récupération du JSON à l'aide de 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
la source
Vous devriez probablement expliquer pourquoi et demander une solution différente car je suis sûr que ce n'est pas la meilleure. Vous pouvez probablement utiliser des attributs data-something, mais je ne suis pas sûr qu'ils puissent contenir une quantité "énorme" de texte. Quant aux caractères spéciaux, vous pouvez simplement encoder (escape () et unescape ()) le texte.
Maiku Mori du
Oui, la limite est de 65536 caractères ( stackoverflow.com/questions/2752457/… )
Maiku Mori
1
Btw, si votre attribut est nommé, data-jsonvous devez utiliser $(this).data('json'), le jQuery vous a couvert sur cette partie.
Ciantic
Juste une note, nommer le suffixe de données à json n'est pas nécessaire. Si vous mettez json valide dans n'importe quel attribut data-custom_attribute, cela fonctionnera correctement avec jQuery.
Garet Claborn
veuillez corriger la séquence d'accolades)}; =>});
MotsManish

Réponses:

41

Le HTML n'a pas à valider.

Pourquoi pas? La validation est un contrôle qualité très simple qui détecte de nombreuses erreurs. Utilisez un data-*attribut HTML 5 .

L'objet JSON peut être de n'importe quelle taille (c'est-à-dire énorme).

Je n'ai vu aucune documentation sur les limites du navigateur pour les tailles d'attributs.

Si vous les rencontrez, stockez les données dans un fichier <script>. Définissez un objet et mappez les éléments idaux noms de propriété de cet objet.

Et si le JSON contient des caractères spéciaux? (par exemple {test: '<"myString />'})

Suivez simplement les règles normales pour inclure des données non fiables dans les valeurs d'attribut. Utilisez &amp;and &quot;(si vous encapsulez la valeur d'attribut entre guillemets doubles) ou &#x27;(si vous encapsulez la valeur d'attribut entre guillemets simples).

Notez, cependant, qu'il ne s'agit pas de JSON (ce qui nécessite que les noms de propriété soient des chaînes et que les chaînes ne soient délimitées que par des guillemets doubles).

Quentin
la source
5
Donc vous dites que je devrais faire htmlentities($json)avant de le mettre dans l'attribut HTML? Et puis comment décoder cela quand je veux le lire dans jQuery? Et puis comment le réécrire en utilisant jQuery de la même manière qu'en PHP?
BadHorsie le
6
Donc vous dites que je devrais faire html_encode ($ json) avant de le mettre dans l'attribut HTML? - si vous utilisez PHP, cela fonctionnerait. Et puis comment décoder cela quand je veux le lire dans jQuery? - Décoder de l'attribut? Le navigateur le fera lorsqu'il analysera le HTML dans un DOM. Et puis comment le réécrire en utilisant jQuery de la même manière qu'en PHP?- Vous définissez les attributs des nœuds DOM, vous ne générez pas de HTML brut, le navigateur s'en chargera.
Quentin le
1
À la minute où j'ai un problème où mon navigateur ne le décode pas actuellement sur Google Chrome, et lorsque je vais analyser JSON, toutes les entités HTML sont là et échouent.
Bradley Weston
Si vous le mettez dans une balise de script, vous devez l'échapper différemment en raison de la gestion spéciale des balises de script. par exemple, une valeur contenant </script> mettrait fin à la balise script.
Dobes Vandermeer
16

Selon l'endroit où vous le mettez,

  • Dans un <div>comme vous l'avez demandé, vous devez vous assurer que le JSON ne contient pas de spéciaux HTML qui pourraient commencer une balise, un commentaire HTML, un doctype intégré, etc. Vous devez au moins échapper <, et& de telle sorte que le caractère d'origine ne apparaissent dans la séquence échappée.
  • Dans les <script>éléments, vous devez vous assurer que le JSON ne contient pas de balise de fin </script>ou de limite de texte d'échappement: <!--ou -->.
  • Dans les gestionnaires d'événements, vous devez vous assurer que le JSON conserve sa signification même s'il contient des éléments qui ressemblent à des entités HTML et ne rompt pas les limites d'attribut ( "ou ').

Pour les deux premiers cas (et pour les anciens analyseurs JSON), vous devez encoder U + 2028 et U + 2029 car ce sont des caractères de nouvelle ligne en JavaScript même s'ils sont autorisés dans les chaînes non codées en JSON.

Pour être correct, vous devez échapper \et citer des caractères JSON et ce n'est jamais une mauvaise idée de toujours encoder NUL.

Si le code HTML peut être diffusé sans encodage de contenu, vous devez encoder + pour éviter les attaques UTF-7 .

Dans tous les cas, la table d'échappement suivante fonctionnera:

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

Ainsi, la valeur de chaîne JSON pour le texte Hello, <World>!avec une nouvelle ligne à la fin serait "Hello, \u003cWorld\u003e!\r\n".

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

Une autre façon de le faire est de mettre les données json dans la <script>balise, mais pas avec type="text/javascript", mais avec type="text/bootstrap"ou type="text/json"type, pour éviter l'exécution de javascript.

Ensuite, à un endroit de votre programme, vous pouvez le demander de cette manière:

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

Côté serveur, vous pouvez faire quelque chose comme ça (cet exemple avec php et twig ):

<script id="my_model" type="text/json">
  {{ my_model|json_encode()|raw }}
</script>
Sergey Kamardin
la source
1
Pour JSON, utilisez le type de script "application / json". C'est aussi bien d'avoir le niveau supérieur comme objet à long terme.
OIS
1
Cela ne répond pas directement à la question de l'op, mais cela m'a toujours été très utile. Les données que je stocke s'appliquent à la page dans son ensemble et non à un élément spécifique, donc un attribut d'élément ne fonctionne pas vraiment (à moins que je ne le mette comme sur l'élément body, ce qui me semble un peu boiteux, d'autant plus que mon les données peuvent être volumineuses). Le stocker dans un <script type="application/json" id="MyData"></script>fonctionne parfaitement. Puis, en utilisant ActiveWAFL / DblEj, je peux le regarder avec: document.GetData("#MyData").
Evan de la Cruz
2
Cette réponse contient des informations fausses / dangereuses! La modification du type de script ne modifie pas la détection des balises </script>. Essayez simplement:<script type="application/json" id="MyData"> "abc</script><script>alert()</script>" </script>
hyperknot
12

Une autre option consiste à encoder en base64 la chaîne JSON et si vous devez l'utiliser dans votre javascript, décodez-la avec la atob()fonction.

var data = JSON.parse(atob(base64EncodedJSON));
Pavel Petrov
la source
Merci pour atob. Je n'ai jamais été au courant de cela!
Ifedi Okonkwo
3
Attention - ne fonctionne pas si JSON contient des caractères non latins.
Realexer
5

Vous pouvez utiliser 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());

Production

Prénom: Jayson Nom: Monterroso

Vérifiez ceci: http://learn.knockoutjs.com/

jayM
la source
2

Rien d'extraordinaire ici. Depuis PHP, exécutez la chaîne JSON htmlspecialcharspour vous assurer qu'aucun caractère spécial ne peut être interprété comme HTML. De Javascript, pas d'échappatoire nécessaire; il suffit de définir l'attribut et vous êtes prêt à partir.

Matchu
la source
2

Pour les objets JSON simples, le code ci-dessous fonctionnerait.

Encoder:

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

Décoder:

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

Ce que vous pouvez faire, c'est utiliser cdata autour de vos éléments comme ceci

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

où mydata est une chaîne json brute. J'espère que cela vous aidera, vous et les autres.

Faiyet
la source
Comment cette solution fonctionne-t-elle (peut-elle)? Et si je veux stocker quelque chose comme ">dans mydata?
Martin Pecka
1
Que faire si la chaîne contient "]]>"
Dobes Vandermeer
1

Une autre pensée qui pourrait être utilisée est de stocker les données JSON sous forme de chaîne base64 dans l'attribut, puis de les utiliser window.atobou window.btoade les restaurer dans des données JSON utilisables.

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