Cách tốt nhất để lưu trữ JSON trong một thuộc tính HTML?


112

Tôi cần đặt một đối tượng JSON vào một thuộc tính trên một phần tử HTML.

  1. HTML không cần phải xác thực.

    Trả lời bởi Quentin: Lưu trữ JSON trong một data-*thuộc tính , là HTML5 hợp lệ.

  2. Đối tượng JSON có thể có kích thước bất kỳ - tức là rất lớn

    Trả lời bởi Maiku Mori: Giới hạn cho một thuộc tính HTML có khả năng là 65536 ký tự .

  3. Điều gì sẽ xảy ra nếu JSON chứa các ký tự đặc biệt? ví dụ {foo: '<"bar/>'}

    Trả lời bởi Quentin: Hãy mã hóa chuỗi JSON trước khi đưa nó vào thuộc tính, theo quy ước thông thường. Đối với PHP, hãy sử dụng hàm . htmlentities()


EDIT - Giải pháp ví dụ sử dụng PHP và jQuery

Viết JSON vào thuộc tính 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>

Truy xuất JSON bằng 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);

});

Bạn có thể nên giải thích tại sao và yêu cầu giải pháp khác vì tôi khá chắc rằng đây không phải là cách tốt nhất. Bạn có thể sử dụng thuộc tính data-something nhưng tôi không chắc liệu chúng có thể chứa một lượng văn bản "khổng lồ" hay không. Đối với các ký tự đặc biệt, bạn chỉ có thể mã hóa (Escape () và unescape ()) văn bản.
Maiku Mori

Vâng hạn là 65536 ký tự ( stackoverflow.com/questions/2752457/... )
Maiku Mori

1
Btw, nếu thuộc tính của bạn được đặt tên data-jsonmà bạn nên sử dụng $(this).data('json'), jQuery sẽ giúp bạn đề cập đến phần đó.
Ciantic

Chỉ cần lưu ý, đặt tên hậu tố dữ liệu thành json là không bắt buộc. Nếu bạn đặt json hợp lệ trong bất kỳ data-custom_attribute nào thì nó sẽ hoạt động tốt với jQuery.
Garet Claborn

hãy sửa lại chuỗi dấu ngoặc nhọn)}; =>});
Tiếng Tây Ban Nha

Câu trả lời:


41

HTML không cần phải xác thực.

Tại sao không? Xác thực là QA thực sự dễ dàng mắc phải rất nhiều lỗi. Sử dụng data-*thuộc tính HTML 5 .

Đối tượng JSON có thể có kích thước bất kỳ (tức là rất lớn).

Tôi chưa thấy bất kỳ tài liệu nào về giới hạn của trình duyệt đối với kích thước thuộc tính.

Nếu bạn gặp phải chúng, thì hãy lưu trữ dữ liệu trong a <script>. Xác định một đối tượng và ánh xạ các phần tử idvới tên thuộc tính trong đối tượng đó.

Điều gì sẽ xảy ra nếu JSON chứa các ký tự đặc biệt? (ví dụ: {test: '<"myString />'})

Chỉ cần tuân theo các quy tắc thông thường để đưa dữ liệu không đáng tin cậy vào các giá trị thuộc tính. Sử dụng &amp;&quot;(nếu bạn đang đặt giá trị thuộc tính trong dấu ngoặc kép) hoặc &#x27;(nếu bạn đặt giá trị thuộc tính trong dấu ngoặc kép).

Tuy nhiên, lưu ý rằng đó không phải là JSON (yêu cầu tên thuộc tính là chuỗi và các chuỗi chỉ được phân cách bằng dấu ngoặc kép).


5
Vì vậy, bạn đang nói tôi nên làm gì htmlentities($json)trước khi đưa nó vào thuộc tính HTML? Và sau đó làm cách nào để giải mã điều đó khi tôi muốn đọc nó trong jQuery? Và sau đó làm cách nào để viết lại nó bằng cách sử dụng jQuery giống như cách mà nó đã có trong PHP?
BadHorsie

6
Vì vậy, bạn đang nói rằng tôi nên làm html_encode ($ json) trước khi tôi đặt nó vào thuộc tính HTML? - nếu bạn đang sử dụng PHP, thì điều đó sẽ hoạt động. Và sau đó làm cách nào để giải mã điều đó khi tôi muốn đọc nó trong jQuery? - Giải mã từ thuộc tính? Trình duyệt sẽ làm điều đó khi nó phân tích cú pháp HTML thành DOM. Và sau đó làm cách nào để viết lại nó bằng cách sử dụng jQuery giống như cách mà nó đã có trong PHP? - Bạn đang thiết lập các thuộc tính của các nút DOM, không tạo HTML thô, trình duyệt sẽ đảm nhận việc đó.
Quentin

1
Tại thời điểm này, tôi gặp sự cố trong đó trình duyệt của tôi hiện không giải mã nó trên Google Chrome và khi tôi phân tích cú pháp JSON, tất cả các thực thể HTML đều ở đó và không thành công.
Bradley Weston

Nếu bạn đặt nó trong thẻ script, bạn phải thoát nó theo cách khác vì cách xử lý đặc biệt của các thẻ script. ví dụ: một giá trị có </script> trong đó sẽ kết thúc thẻ script.
Dobes Vandermeer

16

Tùy thuộc vào nơi bạn đặt nó,

  • Trong một <div>khi bạn hỏi, bạn cần phải đảm bảo rằng các JSON không chứa đặc biệt HTML mà có thể bắt đầu một thẻ, HTML bình luận, nhúng DOCTYPE, vv Bạn cần phải thoát khỏi ít nhất <, và &trong một cách mà các nhân vật ban đầu không xuất hiện trong chuỗi thoát.
  • Trong <script>các phần tử, bạn cần đảm bảo rằng JSON không chứa thẻ kết thúc </script>hoặc ranh giới văn bản thoát: <!--hoặc -->.
  • Trong trình xử lý sự kiện, bạn cần đảm bảo rằng JSON giữ nguyên ý nghĩa của nó ngay cả khi nó có những thứ trông giống như các thực thể HTML và không phá vỡ ranh giới thuộc tính ( "hoặc ').

Đối với hai trường hợp đầu tiên (và đối với trình phân tích cú pháp JSON cũ), bạn nên mã hóa U + 2028 và U + 2029 vì đó là các ký tự dòng mới trong JavaScript mặc dù chúng được cho phép trong các chuỗi không được mã hóa trong JSON.

Để chính xác, bạn cần phải thoát \và JSON trích dẫn các ký tự và không bao giờ là một ý tưởng tồi khi luôn mã hóa NUL.

Nếu HTML có thể được cung cấp mà không có mã hóa nội dung, bạn nên mã hóa +để ngăn chặn các cuộc tấn công UTF-7 .

Trong mọi trường hợp, bảng thoát sau sẽ hoạt động:

  • NUL -> \u0000
  • CR -> \nhoặc\u000a
  • LF -> \rhoặc\u000d
  • " -> \u0022
  • & -> \u0026
  • ' -> \u0027
  • + -> \u002b
  • /-> \/hoặc\u002f
  • < -> \u003c
  • > -> \u003e
  • \-> \\hoặc\u005c
  • U + 2028 ->\u2028
  • U + 2029 -> \u2029

Vì vậy, giá trị chuỗi JSON cho văn bản Hello, <World>!có dòng mới ở cuối sẽ là "Hello, \u003cWorld\u003e!\r\n".


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

14

Một cách khác mà bạn có thể làm - đó là đặt dữ liệu json vào bên trong <script>thẻ, nhưng không phải với type="text/javascript", mà với type="text/bootstrap"hoặc type="text/json"nhập, để tránh thực thi javascript.

Sau đó, ở một số nơi trong chương trình của bạn, bạn có thể yêu cầu nó theo cách sau:

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

Ở phía máy chủ, bạn có thể làm như sau (ví dụ này với php và twig ):

<script id="my_model" type="text/json">
  {{ my_model|json_encode()|raw }}
</script>

1
Đối với JSON, hãy sử dụng loại tập lệnh "application / json". Ngoài ra, thật tốt khi có cấp cao nhất như một đối tượng về lâu dài.
OIS

1
Điều này không trực tiếp trả lời câu hỏi của op nhưng nó vẫn rất hữu ích đối với tôi. Dữ liệu tôi đang lưu trữ áp dụng cho toàn bộ trang chứ không phải cho bất kỳ phần tử cụ thể nào, do đó, một thuộc tính phần tử không thực sự hoạt động (trừ khi tôi đặt nó giống như trên phần tử body, điều này có vẻ hơi tệ đối với tôi đặc biệt là vì dữ liệu có thể lớn). Lưu trữ nó trong một <script type="application/json" id="MyData"></script>công trình hoàn hảo. Sau đó, sử dụng ActiveWAFL / DblEj, tôi có thể nhìn nó với: document.GetData("#MyData").
Evan de la Cruz

2
Câu trả lời này chứa thông tin sai / nguy hiểm! Thay đổi loại tập lệnh không sửa đổi việc phát hiện các thẻ </script>. Chỉ cần thử:<script type="application/json" id="MyData"> "abc</script><script>alert()</script>" </script>
hyperknot

12

Một tùy chọn khác là mã hóa base64 chuỗi JSON và nếu bạn cần sử dụng nó trong javascript, hãy giải mã nó bằng atob()hàm.

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

Cảm ơn vì atob. Tôi chưa bao giờ nhận thức được điều đó!
Ifedi Okonkwo

3
Hãy lưu ý - không hoạt động nếu JSON chứa các ký tự không phải la tinh.
Realexer

5

Bạn có thể sử dụng 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());

Đầu ra

Tên: Jayson Họ: Monterroso

Kiểm tra cái này: http://learn.knockoutjs.com/


2

Không có gì lạ mắt ở đây. Từ PHP, hãy chạy qua chuỗi JSON htmlspecialcharsđể đảm bảo rằng không có ký tự đặc biệt nào có thể được hiểu là HTML. Từ Javascript, không cần thoát; chỉ cần đặt thuộc tính và bạn đã sẵn sàng.


2

Đối với các đối tượng JSON đơn giản, mã bên dưới sẽ hoạt động.

Mã hóa:

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

Giải mã:

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

1

Những gì bạn có thể làm là sử dụng cdata xung quanh / các phần tử của bạn như thế này

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

trong đó mydata là một chuỗi json thô. Hy vọng điều này sẽ giúp bạn và những người khác.


Giải pháp này hoạt động như thế nào? Điều gì sẽ xảy ra nếu tôi muốn lưu trữ một cái gì đó như ">trong mydata?
Martin Pecka

1
Điều gì nếu chuỗi chứa "]]>"
Dobes Vandermeer

1

Một suy nghĩ khác có thể được sử dụng là lưu trữ dữ liệu JSON dưới dạng chuỗi base64 trong thuộc tính và sau đó sử dụng window.atobhoặc window.btoakhôi phục nó thành dữ liệu JSON có thể sử dụng được.

<?php
$json = array("data"=>"Some json data thing");
echo "<div data-json=\"".base64_encode(json_encode($json))."\"></div>";
?>
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.