Eine signifikante Änderung der Marshallisierung der Struktur in json ist nur durch die MarshalJSON () -Methode möglich, wobei dort die vollständige Implementierung der Marshallisierung geschrieben wird. Wie genau? Die Go-Dokumentation gibt keine Antworten oder Empfehlungen darauf und bietet sozusagen völlige Freiheit. Und wie kann man diese Freiheit nutzen, um nicht ein paar Krücken zu häufen, die gesamte Logik auf MarshalJSON () zu übertragen und diese schlechte, ständig wachsende Funktion bei den nächsten json-Anpassungen nicht zu überschreiben?
Die Lösung ist eigentlich einfach:
- Sei ehrlich (ehrlich).
(Der zweite Punkt wird nicht sein, der erste ist genug.)
Es ist dieser Ansatz, der eine Menge verwirrenden Codes von govnokoda , viele Änderungen und eine gewisse Art von Spaß vor einer wichtigen Veröffentlichung erspart . Schauen wir uns nicht das Beispiel in der Dokumentation an, in dem json für ein einfaches int angepasst ist, und die gesamte Modelllogik in mehreren Zeilen, sondern unsere ursprüngliche Aufgabe.
Müssen wir wirklich die Struktur unserer Einrichtung ändern und ein paar Krücken stopfen? Ist es wirklich so, dass die sprachliche Strenge, die eine Eins-zu-Eins-Entsprechung zwischen den JSON-Attributen und der Struktur selbst ermöglicht, plötzlich anfing, uns zu stören?
Die anfängliche Aufgabe besteht darin, solche JSON-Strukturen einiger genehmigter Formate zu erhalten. Im ursprünglichen Problem wird nichts über Krücken gesagt. Es wird über verschiedene Datenstrukturen gesprochen. Und wir verwenden denselben Datentyp (Struktur), um diese Daten zu speichern. Daher muss unsere einzelne Einheit mehrere Darstellungen haben. So haben wir die richtige Interpretation des Problems erhalten.
Wir müssen mehrere Darstellungen für unseren Datentyp machen. Ändern Sie die Konvertierung nicht in json für einen bestimmten Fall, sondern haben Sie im Prinzip mehrere Ansichten, von denen eine die Standardansicht ist.
Wir haben also eine andere Entität - Repräsentation .
Kommen wir zu den Beispielen und tatsächlich zum Code.
Angenommen, wir haben eine Buchhandlung, die Bücher verkauft. Alles basiert auf Microservices, und einer von ihnen liefert Daten zu Anfragen im JSON-Format. Bücher wurden zuerst nur in die Storefront hochgeladen. Dann verbinden wir uns mit verschiedenen Affiliate-Netzwerken und stellen beispielsweise Bücher für Studenten zum Sonderpreis zur Verfügung. Und vor kurzem haben unsere Vermarkter plötzlich beschlossen, Werbemaßnahmen durchzuführen, und sie benötigen auch ihren eigenen Preis und einen anderen eigenen Text. Lassen Sie einen anderen Microservice an der Berechnung der Preise und der Erstellung von Texten beteiligt sein, die einer Datenbank vorgefertigte Daten hinzufügen.
Die Entwicklung unseres Buchmodells hat also eine solche Schande erreicht:
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 }
Das letzte Attribut (Ansicht) ist nicht exportiert (privat), es ist kein Teil der Daten, sondern der Speicherort der Ansicht , die Informationen enthält, in denen das Objekt minimiert wird. Im einfachsten Fall ist dies nur die Schnittstelle {}
type BookView interface{}
Wir können der Schnittstelle unserer Ansicht auch eine Methode hinzufügen, z. B. Prepare (), die in MarshalJSON () aufgerufen wird, und die Ausgabestruktur irgendwie vorbereiten, validieren oder protokollieren.
Beschreiben wir nun unsere Ansichten und die Funktion selbst
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) {
Das Senden und Empfangen von Daten zwischen Strukturen erfolgt nach dem Prinzip der Übereinstimmung der Namen der Attribute, während die Typen nicht genau übereinstimmen müssen. Sie können beispielsweise von int64 aus senden, aber in int akzeptieren, aber nicht in uint.
Der letzte Schritt besteht darin, die installierte Datenansicht mit der vollen Leistung der Standardbeschreibung über json-Tags (`json:"promo,omitempty"`)
Eine sehr wichtige Voraussetzung für die Anwendung dieses Ansatzes ist die obligatorische Registrierung von Modellstrukturen und Mappings. Fügen Sie der Funktion init () hinzu, um sicherzustellen, dass alle Strukturen immer registriert sind.
func init() { gob.Register(Book{}) gob.Register(SiteBookView{}) gob.Register(Partner1BookView{}) gob.Register(Partner2BookView{}) gob.Register(PromoBookView{}) }
Vollständiger Modellcode:
Versteckter Text 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) }
Der Controller hat ungefähr diesen Code:
func GetBooksForPartner2(ctx *gin.Context) { books := LoadBooksForPartner2() for i := range books { books[i].SetPartner2View() } ctx.JSON(http.StatusOK, books) }
Fügen Sie für eine weitere Änderung von json einfach eine weitere Ansicht hinzu und denken Sie daran, sie in init () zu registrieren.