วิธีที่ดีที่สุดในการจัดเก็บ JSON ในแอตทริบิวต์ HTML?


112

ฉันต้องการใส่ออบเจ็กต์ JSON ลงในแอตทริบิวต์ขององค์ประกอบ HTML

  1. HTML ไม่จำเป็นต้องตรวจสอบความถูกต้อง

    ตอบโดย Quentin: จัดเก็บ JSON ในdata-*แอตทริบิวต์ซึ่งเป็น HTML5 ที่ถูกต้อง

  2. ออบเจ็กต์ JSON อาจมีขนาดใดก็ได้เช่นใหญ่

    ตอบโดย Maiku Mori: ขีด จำกัด สำหรับแอตทริบิวต์ 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>

การดึง 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);

});

คุณน่าจะอธิบายได้ว่าทำไมและขอวิธีแก้ปัญหาที่แตกต่างออกไปเพราะฉันค่อนข้างแน่ใจว่านี่ไม่ใช่สิ่งที่ดีที่สุด คุณน่าจะใช้แอตทริบิวต์ data-something ได้ แต่ฉันไม่แน่ใจว่าสามารถเก็บข้อความจำนวนมากได้หรือไม่ สำหรับตัวอักษรพิเศษคุณสามารถเข้ารหัส (escape () และ unescape ()) ข้อความได้
Maiku Mori

ใช่ขีด จำกัด คือ 65536 ตัวอักษร ( stackoverflow.com/questions/2752457/… )
Maiku Mori

1
Btw ถ้าแอตทริบิวต์ของคุณมีชื่อว่าdata-jsonคุณควรใช้$(this).data('json')jQuery คุณได้กล่าวถึงในส่วนนั้นแล้ว
Ciantic

โปรดทราบว่าไม่จำเป็นต้องตั้งชื่อ data-ต่อท้ายเป็น json หากคุณใส่ json ที่ถูกต้องใน data-custom_attribute มันจะทำงานได้ดีกับ jQuery
Garet Claborn

โปรดแก้ไขลำดับการจัดฟัน)}; =>});
MotsManish

คำตอบ:


41

HTML ไม่จำเป็นต้องตรวจสอบความถูกต้อง

ทำไมจะไม่ล่ะ? การตรวจสอบความถูกต้องเป็น QA ที่ง่ายมากที่จับข้อผิดพลาดมากมาย ใช้data-*แอตทริบิวต์HTML 5

ออบเจ็กต์ JSON อาจมีขนาดเท่าใดก็ได้ (เช่นใหญ่)

ฉันไม่เห็นเอกสารใด ๆ เกี่ยวกับการ จำกัด เบราว์เซอร์สำหรับขนาดแอตทริบิวต์

หากคุณพบปัญหาเหล่านี้ให้จัดเก็บข้อมูลในไฟล์<script>. กำหนดอ็อบเจ็กต์และแมปองค์ประกอบids กับชื่อคุณสมบัติในอ็อบเจ็กต์นั้น

จะเกิดอะไรขึ้นถ้า JSON มีอักขระพิเศษ (เช่น {test: '<"myString />'})

เพียงทำตามกฎปกติสำหรับการรวมข้อมูลที่ไม่น่าเชื่อถือในค่าแอตทริบิวต์ ใช้&amp;และ&quot;(หากคุณกำลังตัดค่าแอตทริบิวต์ด้วยเครื่องหมายคำพูดคู่) หรือ&#x27;(หากคุณกำลังตัดค่าแอตทริบิวต์ในเครื่องหมายคำพูดเดี่ยว)

อย่างไรก็ตามโปรดทราบว่านั่นไม่ใช่ JSON (ซึ่งกำหนดให้ชื่อคุณสมบัติเป็นสตริงและสตริงจะถูกคั่นด้วยเครื่องหมายคำพูดคู่เท่านั้น)


5
คุณกำลังบอกว่าฉันควรทำhtmlentities($json)ก่อนที่จะใส่ลงในแอตทริบิวต์ HTML? แล้วฉันจะถอดรหัสได้อย่างไรเมื่อฉันต้องการอ่านใน jQuery? แล้วฉันจะเขียนกลับโดยใช้ jQuery แบบเดียวกับที่เคยเป็นใน PHP ได้อย่างไร?
BadHorsie

6
คุณกำลังบอกว่าฉันควรทำ html_encode ($ json) ก่อนที่จะใส่ลงในแอตทริบิวต์ HTML? - หากคุณใช้ PHP ก็จะได้ผล แล้วฉันจะถอดรหัสได้อย่างไรเมื่อฉันต้องการอ่านใน jQuery? - ถอดรหัสจากแอตทริบิวต์? เบราว์เซอร์จะทำเช่นนั้นเมื่อแยกวิเคราะห์ HTML เป็น DOM แล้วฉันจะเขียนกลับโดยใช้ jQuery แบบเดียวกับที่เคยเป็นใน PHP ได้อย่างไร? - คุณกำลังตั้งค่าแอตทริบิวต์ของโหนด DOM ไม่ได้สร้าง HTML แบบดิบเบราว์เซอร์จะดูแล
Quentin

1
ในขณะนี้ฉันมีปัญหาที่เบราว์เซอร์ของฉันไม่ได้ถอดรหัสบน Google Chrome ในขณะนี้และเมื่อฉันไปแยกวิเคราะห์ JSON เอนทิตี HTML ทั้งหมดจะอยู่ที่นั่นและล้มเหลว
Bradley Weston

หากคุณใส่ไว้ในแท็กสคริปต์คุณต้องหลีกเลี่ยงไม่ให้แตกต่างกันเนื่องจากการจัดการแท็กสคริปต์แบบพิเศษ เช่นค่าที่มี </script> อยู่จะปิดท้ายแท็กสคริปต์
Dobes Vandermeer

16

ขึ้นอยู่กับตำแหน่งที่คุณวาง

  • ใน<div>ขณะที่คุณถามว่าคุณต้องให้แน่ใจว่า JSON ไม่ได้มีพิเศษ HTML ที่สามารถเริ่มต้นแท็กแสดงความคิดเห็น HTML, ประเภทเอกสารที่ฝังตัว ฯลฯ คุณต้องหลบหนีอย่างน้อย<และ&ในลักษณะที่ว่าตัวละครเดิมไม่ได้ ปรากฏในลำดับ Escape
  • ใน<script>องค์ประกอบคุณต้องตรวจสอบให้แน่ใจว่า JSON ไม่มีแท็กปิดท้าย</script>หรือหลีกเลี่ยงขอบเขตข้อความ: <!--หรือ-->.
  • ในตัวจัดการเหตุการณ์คุณต้องตรวจสอบให้แน่ใจว่า JSON คงความหมายไว้แม้ว่าจะมีสิ่งที่ดูเหมือนเอนทิตี HTML และไม่ทำลายขอบเขตแอตทริบิวต์ ( "หรือ')

สำหรับสองกรณีแรก (และสำหรับตัวแยกวิเคราะห์ JSON แบบเก่า) คุณควรเข้ารหัส U + 2028 และ U + 2029 เนื่องจากเป็นอักขระขึ้นบรรทัดใหม่ใน JavaScript แม้ว่าจะได้รับอนุญาตในสตริงที่ไม่มีการเข้ารหัสใน JSON ก็ตาม

เพื่อความถูกต้องคุณต้องใช้ Escape \และอักขระอ้างอิง JSON และไม่ควรเข้ารหัส NUL เสมอไป

หาก HTML อาจจะมีการทำหน้าที่โดยไม่ต้องเข้ารหัสเนื้อหาที่คุณควรเข้ารหัส+เพื่อป้องกันไม่ให้UTF-7 โจมตี

ไม่ว่าในกรณีใดตาราง Escape ต่อไปนี้จะใช้งานได้:

  • 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)}; })); `
Denis Giffeler

14

อีกวิธีหนึ่งที่คุณสามารถทำได้คือใส่ข้อมูล json ไว้ใน<script>แท็ก แต่ไม่ใช่ด้วยtype="text/javascript"แต่มีtype="text/bootstrap"หรือtype="text/json"พิมพ์เพื่อหลีกเลี่ยงการเรียกใช้จาวาสคริปต์

จากนั้นในบางส่วนของโปรแกรมของคุณคุณสามารถขอได้ด้วยวิธีนี้:

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
สิ่งนี้ไม่ได้ตอบคำถามของ op โดยตรง แต่ก็ยังเป็นประโยชน์กับฉันมาก ข้อมูลที่ฉันจัดเก็บใช้กับหน้าโดยรวมและไม่ใช้กับองค์ประกอบเฉพาะใด ๆ ดังนั้นแอตทริบิวต์องค์ประกอบจึงใช้ไม่ได้จริง ๆ (เว้นแต่ฉันจะใส่ไว้ในองค์ประกอบของร่างกายซึ่งดูเหมือนจะเป็นเรื่องง่อยสำหรับฉันโดยเฉพาะอย่างยิ่งเมื่อฉัน ข้อมูลอาจมีขนาดใหญ่) จัดเก็บไว้ใน<script type="application/json" id="MyData"></script>ผลงานได้อย่างสมบูรณ์แบบ จากนั้นใช้ ActiveWAFL / DblEj ฉันสามารถค้นหาได้ด้วย: document.GetData("#MyData").
Evan de la Cruz

2
คำตอบนี้มีข้อมูลเท็จ / เป็นอันตราย! การเปลี่ยนประเภทสคริปต์ไม่ได้แก้ไขการตรวจจับแท็ก </script> แค่ลอง:<script type="application/json" id="MyData"> "abc</script><script>alert()</script>" </script>
hyperknot

12

อีกทางเลือกหนึ่งคือ base64 เข้ารหัสสตริง JSON และหากคุณต้องการใช้ในจาวาสคริปต์ของคุณให้ถอดรหัสด้วยatob()ฟังก์ชัน

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

ขอบคุณสำหรับatob. ฉันไม่เคยรู้มาก่อน!
Ifedi Okonkwo

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

เอาต์พุต

ชื่อจริง: Jayson นามสกุล: Monterroso

ตรวจสอบสิ่งนี้: http://learn.knockoutjs.com/


2

ไม่มีอะไรน่าสนใจที่นี่ จาก PHP ให้รันสตริง JSON htmlspecialcharsเพื่อให้แน่ใจว่าไม่มีอักขระพิเศษใดที่สามารถตีความเป็น HTML ได้ จาก Javascript ไม่จำเป็นต้องหลบหนี เพียงแค่ตั้งค่าแอตทริบิวต์และคุณก็พร้อมที่จะไป


2

สำหรับออบเจ็กต์ JSON ธรรมดาโค้ดด้านล่างจะใช้ได้

เข้ารหัส:

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

ถอดรหัส:

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

1

สิ่งที่คุณสามารถทำได้คือใช้ cdataรอบ ๆ องค์ประกอบของคุณเช่นนี้

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

โดยที่ mydata เป็นสตริง json ดิบ หวังว่านี่จะช่วยคุณและคนอื่น ๆ


วิธีการแก้ปัญหานี้ทำงานอย่างไร? จะทำอย่างไรหากต้องการจัดเก็บข้อมูลเช่น">ใน mydata
Martin Pecka

1
จะเกิดอะไรขึ้นถ้าสตริงมี "]]>"
Dobes Vandermeer

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>";
?>
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.