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

IT技术 javascript php html json
2021-02-26 13:14:00

我需要将 JSON 对象放入 HTML 元素的属性中。

  1. HTML 不必验证。

    Quentin 回答:将 JSON 存储在一个data-*属性中,该属性是有效的 HTML5。

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

    森舞久回答:HTML 属性的限制可能是 65536 个字符

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

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

});
6个回答

HTML 不必验证。

为什么不?验证是非常简单的 QA,它会发现很多错误。使用 HTML 5data-*属性。

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

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

如果确实遇到它们,请将数据存储在<script>. 定义一个对象并将元素ids映射到该对象中的属性名称。

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

只需遵循在属性值中包含不受信任数据的正常规则即可。使用&amp;and &quot;(如果您将属性值括在双引号中)或&#x27;(如果您将属性值括在单引号中)。

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

所以你是说我应该htmlentities($json)在将它放入 HTML 属性之前做些什么?然后当我想在 jQuery 中读取它时如何解码它?然后我如何以与在 PHP 中相同的方式使用 jQuery 写回它?
2021-05-12 13:14:00
如果你把它放在一个脚本标签中,你必须以不同的方式转义它,因为脚本标签的特殊处理。例如,带有 </script> 的值将结束脚本标记。
2021-05-15 13:14:00
目前我遇到了一个问题,我的浏览器目前无法在 Google Chrome 上对其进行解码,当我解析 JSON 时,所有 HTML 实体都在那里并且失败了。
2021-05-16 13:14:00
所以你是说我应该在把它放入 HTML 属性之前做 html_encode($json) ?— 如果您使用的是 PHP,那么它会起作用。然后当我想在 jQuery 中读取它时如何解码它?——从属性解码?浏览器在将 HTML 解析为 DOM 时会这样做。然后我如何以与在 PHP 中相同的方式使用 jQuery 写回它?— 您正在设置 DOM 节点的属性,而不是生成原始 HTML,浏览器会处理它。
2021-05-20 13:14:00

看你把它放在哪里

  • 在 a<div>正如您所问的那样,您需要确保 JSON 不包含可以开始标记、HTML 注释、嵌入的文档类型等的 HTML 特殊内容。您至少需要转义<,并且&以这样的方式原始字符不出现在转义序列中。
  • <script>元素中,您需要确保 JSON 不包含结束标记</script>或转义文本边界:<!---->
  • 在事件处理程序中,您需要确保 JSON 保留其含义,即使它具有看起来像 HTML 实体的内容并且不会破坏属性边界("')。

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

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

如果 HTML 可能在没有内容编码的情况下提供,您应该编码+以防止UTF-7 攻击

在任何情况下,以下转义表都可以使用:

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

因此,末尾Hello, <World>!带有换行符的文本的 JSON 字符串值将是"Hello, \u003cWorld\u003e!\r\n".

return (input.replace(/([\s"'&+\/\\<>\u2028\u2029\u0000])/g, (match, p1) => { return \\u${p1.codePointAt(0).toString(16).padStart(4, 0)}; }));`
2021-04-30 13:14:00

另一种方法是将 json 数据放在<script>标签中,但不是 with type="text/javascript",而是 withtype="text/bootstrap"type="text/json"type ,以避免 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>
对于 JSON,请使用脚本类型“application/json”。从长远来看,将顶级作为一个对象也很好。
2021-05-15 13:14:00
这并没有直接回答操作员的问题,但它对我仍然非常有帮助。我存储的数据适用于整个页面而不适用于任何特定元素,因此元素属性实际上不起作用(除非我把它放在 body 元素上,这对我来说似乎有点蹩脚,尤其是因为我的数据可能很大)。将其<script type="application/json" id="MyData"></script>完美地存储在作品中。然后,使用ActiveWAFL / DblEj,我可以看看它有:document.GetData("#MyData")
2021-05-16 13:14:00
此答案包含虚假/危险信息!更改脚本类型不会修改 </script> 标签的检测。你试一试:<script type="application/json" id="MyData"> "abc</script><script>alert()</script>" </script>
2021-05-21 13:14:00

另一种选择是对 JSON 字符串进行 base64 编码,如果您需要在 javascript 中使用它,请使用该atob()函数对其进行解码

var data = JSON.parse(atob(base64EncodedJSON));
谢谢你atob我从来没有意识到这一点!
2021-05-12 13:14:00
当心 - 如果 JSON 包含非拉丁字符,则不起作用。
2021-05-17 13:14:00

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

编码:

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

解码:

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