125

I need to put a JSON object into an attribute on an HTML element.

  1. The HTML does not have to validate.

    Answered by Quentin: Store the JSON in a data-* attribute, which is valid HTML5.

  2. The JSON object could be any size - i.e. huge

    Answered by Maiku Mori: The limit for an HTML attribute is potentially 65536 characters.

  3. What if the JSON contains special characters? e.g. {foo: '<"bar/>'}

    Answered by Quentin: Encode the JSON string before putting it into the attribute, as per the usual conventions. For PHP, use the htmlentities() function.


EDIT - Example solution using PHP and jQuery

Writing the JSON into the HTML attribute:

<?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>

Retrieving the JSON using 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);

});
Community
  • 1
  • 1
BadHorsie
  • 14,135
  • 30
  • 117
  • 191
  • You should probable explain why and ask for different solution since I'm quite sure this isn't the best. You can prob use data-something attributes but I'm not sure if they can hold "huge" amount of text. As for special chars you can just encode (escape() and unescape()) the text. – Maiku Mori Sep 06 '11 at 16:00
  • Yeah limit is 65536 chars (http://stackoverflow.com/questions/2752457/max-length-of-an-html-attribute-value) – Maiku Mori Sep 06 '11 at 16:02
  • 1
    Btw, if your attribute is named `data-json` you should use `$(this).data('json')`, the jQuery has you covered on that part. – Ciantic Dec 04 '13 at 18:19
  • Just a note, naming the data-suffix to json is not required. If you put valid json in any data-custom_attribute it will work fine with jQuery. – That Realty Programmer Guy Apr 05 '14 at 15:45
  • please fix the braces sequence )}; => }); – MotsManish Jun 09 '16 at 10:31
  • @MotsManish Oops! :) – BadHorsie Jun 09 '16 at 10:37

10 Answers10

41

The HTML does not have to validate.

Why not? Validation is really easy QA that catches lots of mistakes. Use an HTML 5 data-* attribute.

The JSON object could be any size (i.e. huge).

I've not seen any documentation on browser limits to attribute sizes.

If you do run into them, then store the data in a <script>. Define an object and map element ids to property names in that object.

What if the JSON contains special characters? (e.g. {test: '<"myString/>'})

Just follow the normal rules for including untrusted data in attribute values. Use &amp; and &quot; (if you’re wrapping the attribute value in double quotes) or &#x27; (if you’re wrapping the attribute value in single quotes).

Note, however, that that is not JSON (which requires that property names be strings and strings be delimited only with double quotes).

Quentin
  • 914,110
  • 126
  • 1,211
  • 1,335
  • 5
    So you're saying I should do `htmlentities($json)` before I put it into the HTML attribute? And then how do I decode that when I want to read it in jQuery? And then how do I write it back in using jQuery in the same way that it was in PHP? – BadHorsie Sep 06 '11 at 16:01
  • 6
    *So you're saying I should do html_encode($json) before I put it into the HTML attribute?* — if you're using PHP, then that would work. *And then how do I decode that when I want to read it in jQuery?* — Decode from the attribute? The browser will do that when it parses the HTML into a DOM. *And then how do I write it back in using jQuery in the same way that it was in PHP?* — You're setting attributes of DOM nodes, not generating raw HTML, the browser will take care of it. – Quentin Sep 06 '11 at 16:03
  • 2
    At the minute I have an issue where my browser isn't decoding it currently on Google Chrome, and when I go to parse JSON all the HTML entities are the there and fails. – Bradley Weston Aug 26 '13 at 12:02
  • If you put it in a script tag, you have to escape it differently because of the special handling of script tags. e.g. a value with in it would end the script tag. – Dobes Vandermeer Dec 30 '16 at 19:30
19

Another way you can do it – is put json data inside <script> tag, but not with type="text/javascript", but with type="text/bootstrap" or type="text/json" type, to avoid javascript execution.

Then, in some place of your program, you can ask for it in this way:

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

On server side, you can do something like this (this example with php and twig):

<script id="my_model" type="text/json">
  {{ my_model|json_encode()|raw }}
</script>
Sergey Kamardin
  • 1,640
  • 1
  • 18
  • 22
  • 1
    For JSON use the script type "application/json". Also it is nice to have the top level as an object in the long run. – OIS Oct 19 '15 at 14:21
  • 1
    This doesn't directly answer the op's question but it was still very helpful to me. The data I'm storing applies to the page as a whole and not to any specific element, so an element attribute doesn't really work (unless I put it like on the body element, which just seems kinda lame to me especially since my data can be big). Storing it in a `` works perfectly. Then, using ActiveWAFL / DblEj, I can look it up with: `document.GetData("#MyData")`. – Evan de la Cruz Dec 18 '15 at 22:19
  • 2
    This answer contains false / dangerous information! Changing script type doesn't modify the detection of tags. Just try: `" ` – hyperknot Aug 28 '16 at 23:18
17

Depending on where you put it,

  • In a <div> as you asked, you need to ensure that the JSON does not contain HTML specials that could start a tag, HTML comment, embedded doctype, etc. You need to escape at least <, and & in such a way that the original character does not appear in the escaped sequence.
  • In <script> elements you need to ensure that the JSON does not contain an end tag </script> or escaping text boundary: <!-- or -->.
  • In event handlers you need to ensure that the JSON preserves its meaning even if it has things that look like HTML entities and does not break attribute boundaries (" or ').

For the first two cases (and for old JSON parsers) you should encode U+2028 and U+2029 since those are newline characters in JavaScript even though they are allowed in strings unencoded in JSON.

For correctness, you need to escape \ and JSON quote characters and it's never a bad idea to always encode NUL.

If the HTML might be served without a content encoding, you should encode + to prevent UTF-7 attacks.

In any case, the following escaping table will work:

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

So the JSON string value for the text Hello, <World>! with a newline at the end would be "Hello, \u003cWorld\u003e!\r\n".

Mike Samuel
  • 118,113
  • 30
  • 216
  • 245
  • 1
    `return (input.replace(/([\s"'`&+\/\\<>\u2028\u2029\u0000])/g, (match, p1) => { return `\\u${p1.codePointAt(0).toString(16).padStart(4, 0)}`; }));` – Denis Giffeler Aug 27 '17 at 08:22
16

Another option is to base64 encode the JSON string and if you need to use it in your javascript decode it with the atob() function.

var data = JSON.parse(atob(base64EncodedJSON));
Pavel Petrov
  • 847
  • 10
  • 19
8

For simple JSON objects, the code below would work.

Encode:

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

Decode:

var jsonString = unescape(attributeString);
var jsonObject = JSON.parse(jsonString);
Crashalot
  • 33,605
  • 61
  • 269
  • 439
5

You can use 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());

Output

First name: Jayson Last name: Monterroso

Check this: http://learn.knockoutjs.com/

jayM
  • 602
  • 6
  • 16
2

Another thought that could be used is store the JSON data as a base64 string in the attribute and then using window.atob or window.btoa to restore it to usable JSON data.

<?php
$json = array("data"=>"Some json data thing");
echo "<div data-json=\"".base64_encode(json_encode($json))."\"></div>";
?>
Matthew Auld
  • 388
  • 1
  • 3
  • 18
2

Nothing fancy here. From PHP, give the JSON string a run through htmlspecialchars to make sure no special characters can be interpreted as HTML. From Javascript, no escaping necessary; just set the attribute and you're good to go.

Matchu
  • 83,922
  • 18
  • 153
  • 160
1

What you can do is use cdata around your element/s like this

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

where mydata is a raw json string. Hope this helps you and others.

Faiyet
  • 5,341
  • 14
  • 51
  • 66
0

In our case replace ' by &#39; and inserting the json between simple quotes works perfectly for vue:

php:

$data = json_encode($data);
$data = preg_replace("/'/", '&#39;', $data);

html:

<vue_tag :data='<?=$json?>' />
ZiTAL
  • 3,466
  • 8
  • 35
  • 50