قم بتغيير مخطط JSON ديناميكيًا في Go with gob

تغيير كبير في تنظيم الهيكل في json هو ممكن فقط من خلال طريقة MarshalJSON () ، وكتابة التنفيذ الكامل للتنظيم هناك. كيف بالضبط؟ لا تقدم وثائق Go أي إجابات أو توصيات لهذا ، مما يوفر ، إذا جاز التعبير ، حرية كاملة. وكيف يمكن الاستفادة من هذه الحرية حتى لا تتراكم حفنة من العكازات ، وتنقل كل المنطق إلى MarshalJSON () ، ثم لا تكتب هذه الوظيفة الفقيرة المتنامية باستمرار مع تخصيصات json التالية؟


الحل بسيط بالفعل:


  1. أن نكون صادقين (صادقة).

(لن تكون النقطة الثانية ، الأولى كافية.)


هذا هو النهج الذي سيوفر طن من رمز مربك govnokoda ، والكثير من التعديلات ونوع معين من المرح قبل إصدار مهم. لنلقِ نظرة على المثال الوارد في الوثائق ، حيث يتم تخصيص json لإدخال بسيط ، ومنطق النموذج بالكامل في عدة سطور ، ولكن في مهمتنا الأصلية.


هل نحن حقا بحاجة إلى تغيير هيكل منشأتنا وحشد مجموعة من العكازات؟ هل حقا أن صرامة اللغة ، التي تنص على مراسلات فردية بين سمات json والهيكل نفسه ، بدأت فجأة بالتدخل معنا؟


المهمة الأولى هي الحصول على مثل هذه الهياكل JSON لبعض الأشكال المعتمدة. في المشكلة الأصلية لا يوجد شيء يقال عن العكازات. يقال عن هياكل البيانات المختلفة. ونحن نستخدم نفس نوع البيانات (هيكل) لتخزين هذه البيانات. وبالتالي ، يجب أن يكون لدينا كيان واحد عدة عروض. لذلك حصلنا على التفسير الصحيح للمشكلة.


نحتاج إلى تقديم العديد من العروض التوضيحية لنوع البيانات الخاص بنا. لا تقم بتغيير التحويل إلى json لحالة معينة ، ولكن من حيث المبدأ لها عدة طرق عرض ، واحدة منها هي طريقة العرض الافتراضية.


لذلك ، لدينا كيان آخر - التمثيل .


ودعونا نصل إلى الأمثلة ، وفي الواقع ، إلى الكود.


لنفترض أن لدينا مكتبة لبيع الكتب. كل شيء مبني على خدمات micros ، ويقدم أحدهم بيانات عن الطلبات بتنسيق json. تم تحميل الكتب لأول مرة فقط على واجهة المتجر. بعد ذلك ، نتصل بشبكات تابعة مختلفة ، على سبيل المثال ، نوفر كتبًا لطلاب الجامعة بسعر خاص. ومؤخراً ، قرر المسوقون لدينا فجأة إجراء بعض الإجراءات الترويجية ، وهم بحاجة أيضًا إلى سعرهم ونص آخر خاص بهم. دع بعض الخدمات الصغيرة الأخرى تشارك في حساب الأسعار وإعداد النصوص ، التي تضيف البيانات الجاهزة إلى قاعدة البيانات.


لذا ، فإن تطور نموذج كتابنا قد وصل إلى مثل هذا الخزي:


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 BookView interface{} 

يمكننا أيضًا إضافة بعض الأساليب إلى واجهة طريقة العرض الخاصة بنا ، على سبيل المثال Prepare () ، والتي سيتم استدعاؤها في MarshalJSON () وإعداد بطريقة ما أو التحقق من صحة أو تسجيل بنية الإخراج.


الآن دعونا نصف وجهات نظرنا والوظيفة نفسها


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

يحدث إرسال واستقبال البيانات بين الهياكل وفقًا لمبدأ مطابقة أسماء السمات ، في حين أن الأنواع لا يجب أن تتطابق تمامًا ، على سبيل المثال ، يمكنك إرسالها من int64 ، ولكن قبولها في int ، ولكن ليس في uint.


الخطوة الأخيرة هي تنظيم عرض البيانات المثبتة باستخدام القوة الكاملة للوصف القياسي عبر علامات json (`json:"promo,omitempty"`)


أحد المتطلبات الهامة للغاية لتطبيق هذا النهج هو التسجيل الإلزامي للهياكل والتعيينات النموذجية. لضمان أن جميع الهياكل مضمونة دائمًا في التسجيل ، أضفها إلى الدالة init ().


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

رمز الموديل الكامل:


النص المخفي
 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) } 


سيكون لدى وحدة التحكم شيء مثل هذا الرمز:


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

الآن لتغيير json "واحد آخر" ، ما عليك سوى إضافة عرض آخر وتذكر تسجيله في الحرف ().

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


All Articles