Ubah skema JSON secara dinamis di Go with gob

Secara signifikan mengubah virtualisasi struktur di json hanya mungkin melalui metode MarshalJSON (), menulis di sana implementasi penuh dari marshalization. Bagaimana tepatnya? Dokumentasi Go tidak memberikan jawaban atau rekomendasi apa pun untuk ini, dengan ketentuan, dengan demikian, kebebasan penuh. Dan bagaimana memanfaatkan kebebasan ini agar tidak menumpuk banyak kruk, mentransfer semua logika ke MarshalJSON (), dan kemudian tidak menimpa fungsi miskin yang terus berkembang ini dengan kustomisasi json berikutnya?


Solusinya sebenarnya sederhana:


  1. Jujurlah (jujur).

(Poin kedua tidak akan, yang pertama sudah cukup.)


Pendekatan inilah yang akan menghemat satu ton kode membingungkan govnokoda , banyak perubahan dan kesenangan tertentu sebelum rilis penting. Mari kita tidak melihat contoh dalam dokumentasi, di mana json dikustomisasi untuk int sederhana, dan seluruh model logika pada beberapa baris, tetapi pada tugas awal kami.


Apakah kita benar-benar perlu mengubah struktur fasilitas kita dan menjejalkan banyak kruk? Apakah benar-benar kekakuan bahasa, yang menyediakan korespondensi satu-ke-satu antara atribut json dan struktur itu sendiri, tiba-tiba mulai mengganggu kita?


Tugas awal adalah untuk mendapatkan struktur JSON seperti itu dari beberapa format yang disetujui. Dalam masalah aslinya tidak ada yang dikatakan tentang kruk. Dikatakan tentang struktur data yang berbeda. Dan kami menggunakan tipe data yang sama (struct) untuk menyimpan data ini. Dengan demikian, entitas tunggal kami harus memiliki beberapa representasi. Jadi kami mendapat interpretasi yang benar dari masalahnya.


Kita perlu membuat beberapa representasi untuk tipe data kita. Jangan mengubah konversi ke json untuk kasus tertentu, tetapi pada prinsipnya memiliki beberapa tampilan, salah satunya adalah tampilan default.


Jadi, kita memiliki entitas lain - representasi .


Dan mari kita ke contoh dan, sebenarnya, ke kode.


Misalkan kita memiliki toko buku yang menjual buku. Semuanya dibangun di atas layanan microser, dan salah satunya memberikan data tentang permintaan dalam format json. Buku pertama diunggah hanya ke etalase. Kemudian kami terhubung ke berbagai jaringan afiliasi, dan, misalnya, menyediakan buku untuk mahasiswa dengan harga khusus. Dan baru-baru ini, pemasar kami tiba-tiba memutuskan untuk mengadakan semacam tindakan promosi dan mereka juga membutuhkan harga mereka sendiri dan beberapa teks lain dari mereka sendiri. Biarkan beberapa layanan mikro lainnya terlibat dalam perhitungan harga dan persiapan teks, yang menambahkan data yang sudah jadi ke database.


Jadi, evolusi model buku kami telah mencapai aib seperti itu:


type Book struct { Id int64 Title string Description string Partner2Title string Price int64 PromoPrice int64 PromoDescription string Partner1Price int64 Partner2Price int64 UpdatedAt time.Time CreatedAt time.Time view BookView } 

Atribut terakhir (tampilan) adalah non-diekspor (pribadi), itu bukan bagian dari data, tetapi merupakan lokasi penyimpanan tampilan , yang berisi informasi di mana objek json akan dilipat. Dalam kasus paling sederhana, ini hanya antarmuka {}


 type BookView interface{} 

Kami juga dapat menambahkan beberapa metode ke antarmuka tampilan kami, misalnya Siapkan (), yang akan dipanggil di MarshalJSON () dan entah bagaimana menyiapkan, memvalidasi, atau mencatat struktur output.


Sekarang mari kita gambarkan pandangan kita dan fungsinya sendiri


 type SiteBookView struct { Id int64 `json:"sku"` Title string `json:"title"` Description string `json:"description"` Price int64 `json:"price"` } type Partner1BookView struct { Id int64 `json:"bid"` Title string `json:"title"` Partner1Price int64 `json:"price"` } type Partner2BookView struct { Id int64 `json:"id"` Partner2Title string `json:"title"` Description string `json:"description"` Partner2Price int64 `json:"price"` } type PromoBookView struct { Id int64 `json:"ref"` Title string `json:"title"` Description string `json:"description"` PromoPrice int64 `json:"price"` PromoDescription string `json:"promo,omitempty"` } func (b Book) MarshalJSON() (data []byte, err error) { // ,    if b.view == nil { // ,      b.SetDefaultView() } //        var buff bytes.Buffer //   ,            enc := gob.NewEncoder(&buff) //  ,      ,    dec := gob.NewDecoder(&buff) //     err = enc.Encode(b) if err != nil { return } //     err = dec.Decode(b.view) if err != nil { return } //    return json.Marshal(b.view) } 

Mengirim dan menerima data antar struktur terjadi sesuai dengan prinsip pencocokan nama atribut, sedangkan tipe tidak harus sama persis, misalnya, Anda dapat mengirim dari int64, tetapi menerimanya dalam int, tetapi tidak dalam uint.


Langkah terakhir adalah menyusun tampilan data yang diinstal menggunakan kekuatan penuh dari deskripsi standar melalui tag json (`json:"promo,omitempty"`)


Persyaratan yang sangat penting untuk menerapkan pendekatan ini adalah wajib mendaftar struktur model dan pemetaan. Untuk memastikan bahwa semua struktur selalu dijamin terdaftar, tambahkan ke fungsi init ().


 func init() { gob.Register(Book{}) gob.Register(SiteBookView{}) gob.Register(Partner1BookView{}) gob.Register(Partner2BookView{}) gob.Register(PromoBookView{}) } 

Kode model lengkap:


Teks tersembunyi
 import ( "bytes" "encoding/gob" "encoding/json" "time" ) func init() { gob.Register(Book{}) gob.Register(SiteBookView{}) gob.Register(Partner1BookView{}) gob.Register(Partner2BookView{}) gob.Register(PromoBookView{}) } type BookView interface{} type Book struct { Id int64 Title string Description string Partner2Title string Price int64 PromoPrice int64 PromoDescription string Partner1Price int64 Partner2Price int64 UpdatedAt time.Time CreatedAt time.Time view BookView } type SiteBookView struct { Id int64 `json:"sku"` Title string `json:"title"` Description string `json:"description"` Price int64 `json:"price"` } type Partner1BookView struct { Id int64 `json:"bid"` Title string `json:"title"` Partner1Price int64 `json:"price"` } type Partner2BookView struct { Id int64 `json:"id"` Partner2Title string `json:"title"` Description string `json:"description"` Partner2Price int64 `json:"price"` } type PromoBookView struct { Id int64 `json:"ref"` Title string `json:"title"` Description string `json:"description"` PromoPrice int64 `json:"price"` PromoDescription string `json:"promo,omitempty"` } func (b *Book) SetDefaultView() { b.SetSiteView() } func (b *Book) SetSiteView() { b.view = &SiteBookView{} } func (b *Book) SetPartner1View() { b.view = &Partner1BookView{} } func (b *Book) SetPartner2View() { b.view = &Partner2BookView{} } func (b *Book) SetPromoView() { b.view = &PromoBookView{} } func (b Book) MarshalJSON() (data []byte, err error) { if b.view == nil { b.SetDefaultView() } var buff bytes.Buffer enc := gob.NewEncoder(&buff) dec := gob.NewDecoder(&buff) err = enc.Encode(b) if err != nil { return } err = dec.Decode(b.view) if err != nil { return } return json.Marshal(b.view) } 


Pengontrol akan memiliki sesuatu seperti kode ini:


 func GetBooksForPartner2(ctx *gin.Context) { books := LoadBooksForPartner2() for i := range books { books[i].SetPartner2View() } ctx.JSON(http.StatusOK, books) } 

Sekarang untuk "satu lagi" perubahan json, cukup tambahkan tampilan lain dan ingat untuk mendaftarkannya di init ().

Source: https://habr.com/ru/post/id449090/


All Articles