Saya menerima objek JSON dari panggilan AJAX ke server REST. Objek ini memiliki nama properti yang cocok dengan kelas TypeScript saya (ini merupakan lanjutan dari pertanyaan ini ).
Apa cara terbaik untuk menginisialisasi itu? Saya tidak berpikir ini akan berhasil karena kelas (& objek JSON) memiliki anggota yang daftar objek dan anggota yang merupakan kelas, dan kelas-kelas tersebut memiliki anggota yang daftar dan / atau kelas.
Tetapi saya lebih suka pendekatan yang mencari nama anggota dan menugaskan mereka di seluruh, membuat daftar dan membuat instance kelas yang diperlukan, jadi saya tidak perlu menulis kode eksplisit untuk setiap anggota di setiap kelas (ada BANYAK!)
json
typescript
David Thielen
sumber
sumber
Jawaban:
Ini adalah beberapa bidikan cepat untuk menunjukkan beberapa cara berbeda. Mereka sama sekali tidak "lengkap" dan sebagai penafian, saya tidak berpikir itu ide yang baik untuk melakukannya seperti ini. Juga kodenya tidak terlalu bersih karena saya baru saja mengetikkannya agak cepat.
Juga sebagai catatan: Tentu saja kelas deserializable perlu memiliki konstruktor default seperti halnya dalam semua bahasa lain di mana saya mengetahui deserialisasi dalam bentuk apa pun. Tentu saja, Javascript tidak akan mengeluh jika Anda memanggil konstruktor non-default tanpa argumen, tetapi kelas sebaiknya disiapkan untuk itu kemudian (ditambah, itu tidak akan benar-benar menjadi "cara mengetik naskah").
Opsi # 1: Tidak ada informasi run-time sama sekali
Masalah dengan pendekatan ini adalah sebagian besar nama anggota harus sesuai dengan kelasnya. Yang secara otomatis membatasi Anda untuk satu anggota dengan tipe yang sama per kelas dan melanggar beberapa aturan praktik yang baik. Saya sangat menyarankan ini, tetapi hanya daftar di sini karena itu adalah "draft" pertama ketika saya menulis jawaban ini (yang juga mengapa nama-namanya adalah "Foo" dll).
Opsi # 2: Properti nama
Untuk menghilangkan masalah pada opsi # 1, kita perlu memiliki beberapa jenis informasi tentang jenis simpul di objek JSON. Masalahnya adalah bahwa dalam Typescript, hal-hal ini adalah konstruksi waktu kompilasi dan kita membutuhkannya saat runtime - tetapi objek runtime tidak memiliki kesadaran akan sifatnya sampai semuanya ditetapkan.
Salah satu cara untuk melakukannya adalah dengan membuat kelas mengetahui nama mereka. Anda membutuhkan properti ini di JSON juga. Sebenarnya, Anda hanya membutuhkannya di json:
Opsi # 3: Secara eksplisit menyatakan jenis anggota
Seperti yang dinyatakan di atas, informasi tipe anggota kelas tidak tersedia saat runtime - kecuali kita membuatnya tersedia. Kami hanya perlu melakukan ini untuk anggota non-primitif dan kami senang melakukannya:
Opsi # 4: Cara verbose, tapi rapi
Pembaruan 01/03/2016: Seperti yang ditunjukkan oleh @GameAlchemist dalam komentar ( ide , implementasi ), pada Typecript 1.7, solusi yang dijelaskan di bawah ini dapat ditulis dengan cara yang lebih baik menggunakan dekorator kelas / properti.
Serialisasi selalu menjadi masalah dan menurut saya, cara terbaik adalah cara yang tidak terpendek. Dari semua opsi, inilah yang saya lebih suka karena penulis kelas memiliki kontrol penuh atas keadaan objek deserialized. Jika saya harus menebak, saya akan mengatakan bahwa semua opsi lain, cepat atau lambat, akan membuat Anda dalam kesulitan (kecuali Javascript muncul dengan cara asli untuk menangani ini).
Sungguh, contoh berikut ini tidak adil fleksibilitas. Itu benar-benar hanya menyalin struktur kelas. Perbedaan yang harus Anda ingat di sini adalah bahwa kelas memiliki kontrol penuh untuk menggunakan segala jenis JSON yang ingin mengontrol keadaan seluruh kelas (Anda dapat menghitung hal-hal, dll.).
sumber
equals
atautoString
metode (hanya itu yang biasanya Anda buat secara otomatis). Seharusnya tidak terlalu sulit untuk menulis generatordeserialize
jika Anda mau, tetapi itu tidak bisa menjadi otomatisasi run-time.Anda dapat menggunakan
Object.assign
Saya tidak tahu kapan ini ditambahkan, saya saat ini menggunakan Typecript 2.0.2, dan ini tampaknya merupakan fitur ES6.ini dia
HalJson
inilah yang dikatakan chrome
sehingga Anda dapat melihatnya tidak melakukan penugasan secara rekursif
sumber
Object.assign
. Mengapa kita memiliki dua jawaban seperti leksikon di atas yang ini?Object.assign
tidak akan bekerja secara rekursif, dan tidak instantiate jenis objek yang benar, meninggalkan nilai sebagaiObject
instance. Sementara itu baik untuk tugas-tugas sepele, serialisasi tipe kompleks tidak mungkin dengannya. Misalnya, jika properti kelas adalah tipe kelas khusus,JSON.parse
+Object.assign
akan membuat properti itu menjadiObject
. Efek samping termasuk metode dan asesoris yang hilang.Object.assign
, di mana ia masih harus menulis instantiasi bersarang oleh tangan. Pendekatan ini baik untuk objek tingkat tutorial yang sangat sederhana, tetapi tidak untuk penggunaan nyata.TLDR: TypedJSON (bukti kerja konsep)
Akar dari kerumitan masalah ini adalah bahwa kita perlu melakukan deserialisasi JSON saat runtime menggunakan tipe informasi yang hanya ada pada waktu kompilasi . Ini mengharuskan jenis-informasi entah bagaimana dibuat tersedia saat runtime.
Untungnya, ini dapat diselesaikan dengan cara yang sangat elegan dan kuat dengan dekorator dan ReflectDecorators :
Jenis Perekaman-Informasi
Dengan kombinasi ReflectDecorators dan dekorator properti, ketik informasi dapat dengan mudah direkam tentang sebuah properti. Implementasi yang belum sempurna dari pendekatan ini adalah:
Untuk properti apa pun yang diberikan, cuplikan di atas akan menambahkan referensi fungsi konstruktor properti ke
__propertyTypes__
properti tersembunyi pada prototipe kelas. Sebagai contoh:Dan itu saja, kami memiliki informasi jenis yang diperlukan saat runtime, yang sekarang dapat diproses.
Jenis-Informasi Pemrosesan
Pertama-tama kita perlu mendapatkan sebuah
Object
instance menggunakanJSON.parse
- setelah itu, kita dapat melakukan iterate atas entires di__propertyTypes__
(dikumpulkan di atas) dan instantiate properti yang diperlukan sesuai. Jenis objek root harus ditentukan, sehingga deserializer memiliki titik awal.Sekali lagi, implementasi sederhana dari pendekatan ini adalah:
Ide di atas memiliki keuntungan besar deserializing oleh tipe yang diharapkan (untuk nilai kompleks / objek), bukan apa yang ada di JSON. Jika a
Person
diharapkan, maka itu adalahPerson
contoh yang dibuat. Dengan beberapa langkah-langkah keamanan tambahan di tempat untuk tipe primitif dan array, pendekatan ini dapat dibuat aman, yang tahan setiap JSON berbahaya.Kasus Tepi
Namun, jika Anda sekarang senang bahwa solusinya adalah bahwa sederhana, saya memiliki beberapa berita buruk: ada luas sejumlah kasus tepi yang perlu diurus. Hanya beberapa di antaranya adalah:
Jika Anda tidak ingin bermain-main dengan semua ini (saya yakin Anda tidak), saya akan dengan senang hati merekomendasikan versi percobaan yang berfungsi dari pembuktian konsep menggunakan pendekatan ini, TypedJSON - yang saya buat untuk mengatasi masalah ini, masalah yang saya hadapi setiap hari.
Karena bagaimana dekorator masih dianggap eksperimental, saya tidak akan merekomendasikan menggunakannya untuk penggunaan produksi, tetapi sejauh ini itu membantu saya dengan baik.
sumber
Saya telah menggunakan orang ini untuk melakukan pekerjaan: https://github.com/weichx/cerialize
Ini sangat sederhana namun kuat. Ini mendukung:
Contoh:
sumber
Saya telah membuat alat yang menghasilkan antarmuka TypeScript dan "ketik peta" runtime untuk melakukan pemeriksaan ketik runtime terhadap hasil
JSON.parse
: ts.quicktype.ioMisalnya, diberikan JSON ini:
quicktype menghasilkan antarmuka TypeScript berikut dan ketik peta:
Kemudian kami memeriksa hasil
JSON.parse
terhadap tipe peta:Saya telah meninggalkan beberapa kode, tetapi Anda dapat mencoba mengetik cepat untuk detailnya.
sumber
Opsi # 5: Menggunakan konstruktor Typescript dan jQuery.extend
Ini tampaknya menjadi metode yang paling dipelihara: menambahkan konstruktor yang mengambil sebagai parameter struktur json, dan memperluas objek json. Dengan begitu, Anda dapat mem-parsing struktur json ke dalam seluruh model aplikasi.
Tidak perlu membuat antarmuka, atau mendaftar properti di konstruktor.
Dalam panggilan balik ajax Anda tempat Anda menerima perusahaan untuk menghitung gaji:
sumber
$.extend
datang?Object.assign
, yang menghapus ketergantungan ini ke jQuery.Untuk objek sederhana, saya suka metode ini:
Memanfaatkan kemampuan untuk mendefinisikan properti di konstruktor memungkinkannya menjadi singkat.
Ini memberi Anda objek yang diketik (vs semua jawaban yang menggunakan Object.assign atau beberapa varian, yang memberi Anda Obyek) dan tidak memerlukan perpustakaan atau dekorator eksternal.
sumber
Opsi ke-4 yang dijelaskan di atas adalah cara sederhana dan menyenangkan untuk melakukannya, yang harus dikombinasikan dengan opsi ke-2 dalam kasus di mana Anda harus menangani hierarki kelas seperti misalnya daftar anggota yang merupakan salah satu dari kejadian subkelas dari kelas super Anggota, mis. Direktur memperpanjang Anggota atau Siswa memperpanjang Anggota. Dalam hal ini Anda harus memberikan tipe subclass dalam format json
sumber
JQuery .extend melakukan ini untuk Anda:
sumber
yang terbaik yang saya temukan untuk tujuan ini adalah transformator kelas. github.com/typestack/class-transformer
Begitulah cara Anda menggunakannya:
Beberapa kelas:
Jika Anda menggunakan properti bersarang dekorator @Type akan dibuat juga.
sumber
Mungkin bukan solusi aktual, tetapi sederhana:
bekerja untuk dependensi yang sulit juga !!!
sumber
baz
akan menjadi tipeObject
dan bukan tipeBar.
Ini berfungsi dalam kasus sederhana ini karenaBar
tidak memiliki metode (hanya properti primitif). JikaBar
memiliki metode sepertiisEnabled()
, pendekatan ini akan gagal karena metode itu tidak akan berada dalam string JSON berseri.Pilihan lain menggunakan pabrik
https://github.com/MrAntix/ts-deserialize
gunakan seperti ini
sumber
Saya pribadi lebih suka opsi # 3 dari @Ingo Bürk. Dan saya memperbaiki kodenya untuk mendukung berbagai data kompleks dan array data primitif.
sumber
Anda bisa melakukannya seperti di bawah ini
dan
sumber
sumber