Cara terbaik untuk menyimpan JSON dalam atribut HTML?

112

Saya perlu memasukkan objek JSON ke dalam atribut pada elemen HTML.

  1. HTML tidak harus memvalidasi.

    Dijawab oleh Quentin: Simpan JSON dalam data-*atribut , yang merupakan HTML5 yang valid.

  2. Objek JSON bisa berukuran berapa saja - yaitu sangat besar

    Dijawab oleh Maiku Mori: Batas untuk atribut HTML kemungkinan adalah 65536 karakter .

  3. Bagaimana jika JSON berisi karakter khusus? misalnya {foo: '<"bar/>'}

    Dijawab oleh Quentin: Encode string JSON sebelum memasukkannya ke dalam atribut, seperti konvensi biasa. Untuk PHP, gunakan fungsi . htmlentities()


EDIT - Contoh solusi menggunakan PHP dan jQuery

Menulis JSON ke dalam atribut 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>

Mengambil JSON menggunakan 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
sumber
Anda mungkin harus menjelaskan mengapa dan meminta solusi yang berbeda karena saya cukup yakin ini bukan yang terbaik. Anda mungkin dapat menggunakan atribut data-something tetapi saya tidak yakin apakah atribut tersebut dapat menampung sejumlah besar teks. Sedangkan untuk karakter khusus Anda cukup menyandikan (escape () dan unescape ()) teks.
Maiku Mori
Ya batasnya adalah 65536 karakter ( stackoverflow.com/questions/2752457/… )
Maiku Mori
1
Btw, jika atribut data-jsonAnda diberi nama, Anda harus menggunakan $(this).data('json'), jQuery telah Anda bahas di bagian itu.
Ciantic
Sekadar catatan, menamai data-suffix ke json tidak diperlukan. Jika Anda meletakkan json yang valid di data-custom_attribute, itu akan berfungsi dengan baik dengan jQuery.
Garet Claborn
tolong perbaiki urutan kawat gigi)}; =>});
MotsManish

Jawaban:

41

HTML tidak harus memvalidasi.

Kenapa tidak? Validasi QA sangat mudah yang menangkap banyak kesalahan. Gunakan data-*atribut HTML 5 .

Objek JSON bisa berukuran berapa saja (yaitu sangat besar).

Saya belum melihat dokumentasi apa pun tentang batasan browser untuk ukuran atribut.

Jika Anda menemui mereka, maka simpan data di file <script>. Definisikan sebuah objek dan petakan elemen idke nama properti di objek itu.

Bagaimana jika JSON berisi karakter khusus? (mis. {test: '<"myString />'})

Ikuti saja aturan normal untuk memasukkan data tidak tepercaya dalam nilai atribut. Gunakan &amp;dan &quot;(jika Anda membungkus nilai atribut dalam tanda kutip ganda) atau &#x27;(jika Anda membungkus nilai atribut dalam tanda kutip tunggal).

Namun, perhatikan bahwa itu bukan JSON (yang mengharuskan nama properti berupa string dan string hanya dipisahkan dengan tanda kutip ganda).

Quentin
sumber
5
Jadi maksud Anda, saya harus melakukannya htmlentities($json)sebelum memasukkannya ke dalam atribut HTML? Lalu bagaimana cara memecahkan kode itu ketika saya ingin membacanya di jQuery? Lalu bagaimana cara menuliskannya kembali menggunakan jQuery dengan cara yang sama seperti di PHP?
BadHorsie
6
Jadi maksud Anda saya harus melakukan html_encode ($ json) sebelum saya memasukkannya ke dalam atribut HTML? - jika Anda menggunakan PHP, maka itu akan berhasil. Lalu bagaimana cara memecahkan kode itu ketika saya ingin membacanya di jQuery? - Decode dari atribut? Browser akan melakukannya saat mengurai HTML menjadi DOM. Lalu bagaimana cara menuliskannya kembali menggunakan jQuery dengan cara yang sama seperti di PHP? - Anda menyetel atribut simpul DOM, bukan membuat HTML mentah, browser akan menanganinya.
Quentin
1
Saat ini saya memiliki masalah di mana browser saya tidak mendekodekannya saat ini di Google Chrome, dan ketika saya pergi untuk mengurai JSON semua entitas HTML ada di sana dan gagal.
Bradley Weston
Jika Anda memasukkannya ke dalam tag skrip, Anda harus menghindarinya secara berbeda karena penanganan khusus dari tag skrip. misalnya nilai dengan </script> di dalamnya akan mengakhiri tag skrip.
Dobes Vandermeer
16

Tergantung di mana Anda menaruhnya,

  • Dalam <div>seperti yang Anda minta, Anda perlu memastikan bahwa JSON tidak mengandung spesial HTML yang bisa memulai tag, komentar HTML, tertanam doctype, dll Anda perlu untuk melarikan diri setidaknya <, dan& sedemikian rupa bahwa karakter asli tidak muncul di urutan lolos.
  • Dalam <script>elemen, Anda perlu memastikan bahwa JSON tidak berisi tag akhir </script>atau keluar dari batas teks: <!--atau -->.
  • Dalam event handler, Anda perlu memastikan bahwa JSON mempertahankan maknanya meskipun memiliki hal-hal yang terlihat seperti entitas HTML dan tidak melanggar batas atribut ( "atau ').

Untuk dua kasus pertama (dan untuk pengurai JSON lama), Anda harus mengenkode U + 2028 dan U + 2029 karena itu adalah karakter baris baru di JavaScript meskipun diizinkan dalam string yang tidak dikodekan di JSON.

Untuk kebenaran, Anda perlu melarikan diri \dan karakter kutipan JSON dan tidak pernah merupakan ide yang buruk untuk selalu menyandikan NUL.

Jika HTML mungkin disajikan tanpa pengkodean konten, Anda harus melakukan enkode +untuk mencegah serangan UTF-7 .

Bagaimanapun, tabel pelolosan berikut akan berfungsi:

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

Jadi nilai string JSON untuk teks Hello, <World>!dengan baris baru di akhir adalah "Hello, \u003cWorld\u003e!\r\n".

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

Cara lain Anda dapat melakukannya - adalah meletakkan data json di dalam <script>tag, tetapi tidak dengan type="text/javascript", tetapi dengan type="text/bootstrap"atau type="text/json"ketik, untuk menghindari eksekusi javascript.

Kemudian, di beberapa tempat program Anda, Anda dapat memintanya dengan cara ini:

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

Di sisi server, Anda dapat melakukan sesuatu seperti ini (contoh ini dengan php dan ranting ):

<script id="my_model" type="text/json">
  {{ my_model|json_encode()|raw }}
</script>
Sergey Kamardin
sumber
1
Untuk JSON gunakan jenis script "application / json". Juga bagus untuk memiliki level teratas sebagai objek dalam jangka panjang.
OIS
1
Ini tidak secara langsung menjawab pertanyaan op tetapi masih sangat membantu saya. Data yang saya simpan berlaku untuk halaman secara keseluruhan dan tidak untuk elemen tertentu, jadi atribut elemen tidak benar-benar berfungsi (kecuali saya meletakkannya seperti pada elemen tubuh, yang sepertinya agak timpang bagi saya terutama karena saya data bisa menjadi besar). Menyimpannya dalam sebuah <script type="application/json" id="MyData"></script>karya dengan sempurna. Kemudian, menggunakan ActiveWAFL / DblEj, saya bisa melihat itu dengan: document.GetData("#MyData").
Evan de la Cruz
2
Jawaban ini mengandung informasi palsu / berbahaya! Mengubah jenis skrip tidak mengubah deteksi tag </script>. Coba saja:<script type="application/json" id="MyData"> "abc</script><script>alert()</script>" </script>
hyperknot
12

Pilihan lainnya adalah dengan base64 menyandikan string JSON dan jika Anda perlu menggunakannya dalam javascript, dekode dengan atob()fungsi tersebut.

var data = JSON.parse(atob(base64EncodedJSON));
Pavel Petrov
sumber
Terima kasih untuk atob. Saya tidak pernah menyadarinya!
Ifedi Okonkwo
3
Hati-hati - tidak berfungsi jika JSON berisi karakter non latin.
Realexer
5

Anda bisa menggunakan 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());

Keluaran

Nama depan: Jayson Nama belakang: Monterroso

Periksa ini: http://learn.knockoutjs.com/

jayM
sumber
2

Tidak ada yang mewah di sini. Dari PHP, jalankan string JSON htmlspecialcharsuntuk memastikan tidak ada karakter khusus yang dapat diinterpretasikan sebagai HTML. Dari Javascript, tidak perlu keluar; cukup setel atributnya dan Anda siap melakukannya.

Matchu
sumber
2

Untuk objek JSON sederhana, kode di bawah ini akan berfungsi.

Menyandi:

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

Membaca sandi:

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

Apa yang dapat Anda lakukan adalah menggunakan cdata di sekitar elemen Anda seperti ini

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

dimana mydata adalah string json mentah. Semoga ini bisa membantu Anda dan orang lain.

Faiyet
sumber
Bagaimana (dapat) solusi ini bekerja? Bagaimana jika saya ingin menyimpan sesuatu seperti ">di mydata?
Martin Pecka
1
Bagaimana jika string berisi "]]>"
Dobes Vandermeer
1

Pemikiran lain yang dapat digunakan adalah menyimpan data JSON sebagai string base64 dalam atribut dan kemudian menggunakan window.atobatau window.btoamengembalikannya ke data JSON yang dapat digunakan.

<?php
$json = array("data"=>"Some json data thing");
echo "<div data-json=\"".base64_encode(json_encode($json))."\"></div>";
?>
Matthew D Auld
sumber