在HTML属性中存储JSON的最佳方式


Best way to store JSON in an HTML attribute?

我需要把一个JSON对象放到一个HTML元素的属性中。

  1. HTML不需要验证

    answer by Quentin:将JSON存储在data-*属性中,这是有效的HTML5

  2. JSON对象可以是任何大小-即巨大的

    HTML属性的限制可能是65536个字符

  3. 如果JSON包含特殊字符怎么办?例: {foo: '<"bar/>'}

    answer by Quentin:按照通常的惯例,在将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>

使用jQuery检索JSON:

$('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);
});

HTML不需要验证

为什么不呢?验证是非常简单的QA,它可以捕获许多错误。使用HTML 5 data-*属性

JSON对象可以是任意大小(即巨大)。

我还没有看到任何关于浏览器限制属性大小的文档。

如果你遇到它们,那么将数据存储在<script>中。定义一个对象,并将元素id s映射到该对象中的属性名。

如果JSON包含特殊字符怎么办?(例如{test: '<"myString/>'})

只需遵循在属性值中包含不可信数据的常规规则。使用&amp;&quot;(如果你用双引号包装属性值)或&#x27;(如果你用单引号包装属性值)。

但是,请注意,这不是JSON(它要求属性名是字符串,字符串只能用双引号分隔)。

另一种方法是将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>

取决于你把它放在哪里,

  • <div>中,如您所要求的,您需要确保JSON不包含可以启动标签,HTML注释,嵌入doctype等的HTML特殊内容。您至少需要转义<&,以使原始字符不出现在转义序列中。
  • <script>元素中,您需要确保JSON不包含结束标签</script>或转义文本边界:<!---->
  • 在事件处理程序中,您需要确保JSON保留其含义,即使它有看起来像HTML实体的东西,并且不打破属性边界("')。

对于前两种情况(以及旧的JSON解析器),您应该编码U+2028和U+2029,因为它们在JavaScript中是换行字符,即使它们在JSON中未编码的字符串中是允许的。

为了正确起见,您需要转义'和JSON引号字符,并且始终编码NUL永远不是一个坏主意。

如果HTML可能没有内容编码,你应该编码+来防止UTF-7攻击。

在任何情况下,下面的转义表都可以工作:

  • null -> '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" .

另一个选择是base64编码JSON字符串,如果你需要使用它在你的javascript解码它与atob()函数。

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

对于简单的JSON对象,下面的代码可以工作。

编码:

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

解码:

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

你可以使用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());

名字:Jayson姓:Monterroso

检查:http://learn.knockoutjs.com/

这里没什么特别的。在PHP中,通过htmlspecialchars运行JSON字符串,以确保没有特殊字符可以被解释为HTML。从Javascript中,不需要转义;只要设置属性,就可以了。

另一个可以使用的想法是将JSON数据作为base64字符串存储在属性中,然后使用window.atobwindow.btoa将其恢复为可用的JSON数据。

<?php
$json = array("data"=>"Some json data thing");
echo "<div data-json='"".base64_encode(json_encode($json))."'"></div>";
?>

你所能做的就是像这样在元素/s周围使用cdata

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

,其中mydata是一个原始json字符串。希望这对你和其他人有所帮助。

在我们的例子中,用&#39;替换',在简单的引号之间插入json对vue来说是完美的:

php:

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

<vue_tag :data='<?=$json?>' />