使用Gob在Go中动态更改JSON模式

只有通过MarshalJSON()方法,在那里编写编组的完整实现,才能显着更改json中结构的编组。 到底如何 Go文档没有为此提供任何答案或建议,可以提供完全的自由。 以及如何利用这种自由,以免堆积一堆拐杖,将所有逻辑转移到MarshalJSON(),然后不使用下一个json定制来覆盖这个不断增长的不良功能?


解决方案实际上很简单:


  1. 诚实(诚实)。

(第二点不会,第一点就足够了。)


正是这种方法可以在重要的版本发布之前节省大量的govnokoda令人困惑的代码, 许多更改和某种乐趣。 让我们不要看文档中的示例,在该示例中,json是为一个简单的int定制的,整个模型逻辑都在几行上,而是我们的原始任务。


我们真的需要改变我们设施的结构并塞满拐杖吗? 确实,在json属性和结构本身之间一一对应的严格语言突然开始干扰我们吗?


最初的任务是获取某些批准格式的JSON结构。 在最初的问题中,没有提到拐杖。 据说关于不同的数据结构。 并且我们使用相同的数据类型(结构)来存储此数据。 因此,我们的单个实体必须具有多个表示形式。 因此,我们对问题有了正确的解释。


我们需要为我们的数据类型做几种表示。 在特定情况下,请勿将转换更改为json,但是基本上有几个视图,其中之一是默认视图。


因此,我们有另一个实体- 表示形式


让我们看一下示例,实际上是代码。


假设我们有一家卖书的书店。 一切都建立在微服务上,其中之一以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 } 

最后一个属性(视图)是未导出的(私有),它不是数据的一部分,而是仅视图的存储位置,该视图包含将对象折叠在其中的json信息。 在最简单的情况下,这只是接口{}


 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"`)已安装的数据视图(`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更改,只需添加另一个视图,并记住将其注册在init()中即可。

Source: https://habr.com/ru/post/zh-CN449090/


All Articles