Лучший способ сохранить JSON в атрибуте HTML?

112

Мне нужно поместить объект JSON в атрибут элемента HTML.

  1. HTML не требует проверки.

    Ответил Квентин: Сохраните JSON в data-*атрибуте , который является допустимым HTML5.

  2. Объект JSON может быть любого размера - т.е. огромным.

    Ответ Майку Мори: Максимальное значение атрибута HTML потенциально составляет 65536 символов .

  3. Что, если JSON содержит специальные символы? например {foo: '<"bar/>'}

    Отвечено Квентином: закодируйте строку JSON, прежде чем помещать ее в атрибут, в соответствии с обычными соглашениями. Для PHP используйте функцию . htmlentities()


РЕДАКТИРОВАТЬ - Пример решения с использованием PHP и jQuery

Запись JSON в атрибут 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>

Получение JSON с помощью 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
источник
Вероятно, вы должны объяснить почему и попросить другое решение, поскольку я совершенно уверен, что это не лучшее. Вы можете использовать атрибуты данных, но я не уверен, могут ли они содержать «огромное» количество текста. Что касается специальных символов, вы можете просто закодировать (escape () и unescape ()) текст.
Майку Мори,
Да, ограничение составляет 65536 символов ( stackoverflow.com/questions/2752457/… )
Майку Мори
1
Кстати, если ваш атрибут назван, data-jsonвы должны использовать $(this).data('json'), jQuery предоставит вам эту часть.
Ciantic 04
Просто примечание: указывать суффикс данных для json не требуется. Если вы поместите действительный json в любой атрибут data-custom_attribute, он будет нормально работать с jQuery.
Гарет Клэборн
исправьте, пожалуйста, последовательность фигурных скобок)}; =>});
MotsManish 09

Ответы:

41

HTML не требует проверки.

Почему нет? Валидация - это действительно простой QA, который выявляет множество ошибок. Используйте data-*атрибут HTML 5 .

Объект JSON может быть любого размера (т.е. огромным).

Я не видел документации по ограничениям браузера на размеры атрибутов.

Если вы все же столкнетесь с ними, сохраните данные в файле <script>. Определите объект и сопоставьте элемент ids с именами свойств в этом объекте.

Что, если JSON содержит специальные символы? (например, {test: '<"myString />'})

Просто следуйте обычным правилам включения ненадежных данных в значения атрибутов. Используйте &amp;и &quot;(если вы заключаете значение атрибута в двойные кавычки) или &#x27;(если вы заключаете значение атрибута в одинарные кавычки).

Однако обратите внимание, что это не JSON (который требует, чтобы имена свойств были строками, а строки были разделены только двойными кавычками).

Quentin
источник
5
Значит, вы говорите, что я должен сделать это, htmlentities($json)прежде чем помещать это в атрибут HTML? А как потом расшифровать это, если я хочу прочитать это в jQuery? И как потом записать его обратно с помощью jQuery так же, как это было в PHP?
BadHorsie 06
6
Так вы говорите, что я должен сделать html_encode ($ json), прежде чем помещать его в атрибут HTML? - если вы используете PHP, это сработает. А как потом расшифровать это, если я хочу прочитать это в jQuery? - Расшифровать по атрибуту? Браузер сделает это, когда проанализирует HTML в DOM. И как потом записать его обратно с помощью jQuery так же, как это было в PHP? - Вы устанавливаете атрибуты узлов DOM, а не генерируете необработанный HTML, браузер позаботится об этом.
Квентин
1
В данный момент у меня проблема, когда мой браузер не декодирует его в настоящее время в Google Chrome, и когда я перехожу к синтаксическому анализу JSON, все объекты HTML присутствуют и терпят неудачу.
Брэдли Уэстон,
Если вы поместите его в тег скрипта, вам придется экранировать его иначе из-за специальной обработки тегов скрипта. например, значение с </script> в нем заканчивало бы тег скрипта.
Добс Вандермейер,
16

В зависимости от того, куда вы его положите,

  • В , <div>как вы просили, вы должны убедиться , что JSON не содержит HTML - события , которые могли бы начать тег, HTML комментарий, встроенный доктайп и т.д. Вам нужно бежать по меньшей мере ,< , и &таким образом , что первоначальный характер не появляются в экранированной последовательности.
  • В <script>элементах необходимо убедиться, что JSON не содержит закрывающего тега </script>или экранирующей границы текста: <!--или -->.
  • В обработчиках событий вам необходимо убедиться, что JSON сохраняет свое значение, даже если в нем есть вещи, которые выглядят как объекты HTML и не нарушают границы атрибутов ( "или ').

Для первых двух случаев (и для старых парсеров JSON) вы должны кодировать U + 2028 и U + 2029, поскольку это символы новой строки в JavaScript, даже если они разрешены в строках, не закодированных в JSON.

Для корректности вам нужно экранировать \символы кавычек JSON, и никогда не бывает плохой идеей всегда кодировать NUL.

Если HTML-код может обслуживаться без кодировки содержимого, вы должны кодировать, +чтобы предотвратить атаки UTF-7 .

В любом случае будет работать следующая экранирующая таблица:

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

Таким образом, строковое значение JSON для текста Hello, <World>!с новой строкой в конце будет "Hello, \u003cWorld\u003e!\r\n".

Майк Сэмюэл
источник
1
return (input.replace(/([\s"'& + \ / \\ <> \ u2028 \ u2029 \ u0000]) / g, (совпадение, p1) => {return \\u${p1.codePointAt(0).toString(16).padStart(4, 0)}; })); `
Денис Гиффелер
14

Другой способ сделать это - поместить данные json внутри <script>тега, но не с type="text/javascript", а с type="text/bootstrap"или type="text/json"типом, чтобы избежать выполнения javascript.

Затем в каком-то месте вашей программы вы можете запросить это следующим образом:

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

На стороне сервера вы можете сделать что-то вроде этого (этот пример с php и twig ):

<script id="my_model" type="text/json">
  {{ my_model|json_encode()|raw }}
</script>
Сергей Камардин
источник
1
Для JSON используйте тип сценария «application / json». Также приятно иметь верхний уровень как объект в долгосрочной перспективе.
OIS
1
Это не дает прямого ответа на вопрос оператора, но все же мне очень помогло. Данные, которые я храню, применяются к странице в целом, а не к какому-либо конкретному элементу, поэтому атрибут элемента на самом деле не работает (если я не помещаю его, как на элемент body, который мне кажется немного хромым, особенно с учетом того, что мой данные могут быть большими). Хранение в a <script type="application/json" id="MyData"></script>отлично работает. Затем, используя ActiveWAFL / DblEj, я могу посмотреть его с: document.GetData("#MyData").
Эван де ла Крус
2
Этот ответ содержит ложную / опасную информацию! Изменение типа скрипта не влияет на обнаружение тегов </script>. Просто попробуйте:<script type="application/json" id="MyData"> "abc</script><script>alert()</script>" </script>
Hyperknot
12

Другой вариант - base64 закодировать строку JSON, и если вам нужно использовать ее в своем javascript, декодируйте ее с помощью atob()функции.

var data = JSON.parse(atob(base64EncodedJSON));
Павел Петров
источник
Спасибо за atob. Я никогда не знал об этом!
Ifedi Okonkwo 07
3
Осторожно - не работает, если JSON содержит нелатинские символы.
Realexer
5

Вы можете использовать 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());

Вывод

Имя: Джейсон Фамилия: Монтеррозо

Проверьте это: http://learn.knockoutjs.com/

ДжейМ
источник
2

Здесь ничего особенного. Из PHP пропустите строку JSON, htmlspecialcharsчтобы убедиться, что никакие специальные символы не могут быть интерпретированы как HTML. Из Javascript экранировать не нужно; просто установите атрибут, и все готово.

Matchu
источник
2

Для простых объектов JSON подойдет приведенный ниже код.

Encode:

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

Расшифровать:

var jsonString = unescape(attributeString);
var jsonObject = JSON.parse(jsonString);
Crashalot
источник
1

Что вы можете сделать, так это использовать cdata вокруг ваших элементов, например

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

где mydata - это необработанная строка json. Надеюсь, это поможет вам и другим.

Файет
источник
Как работает (может) это решение? Что, если я хочу сохранить что-то подобное ">в своих данных?
Мартин Пека
1
Что, если строка содержит "]]>"
Добс Вандермейр
1

Еще одна мысль, которую можно было бы использовать, - это сохранить данные JSON в виде строки base64 в атрибуте, а затем использовать window.atobили window.btoaвосстановить их до пригодных для использования данных JSON.

<?php
$json = array("data"=>"Some json data thing");
echo "<div data-json=\"".base64_encode(json_encode($json))."\"></div>";
?>
Мэтью Д Олд
источник