一种处理所有嵌入一个通用结构的结构类型的方法(json编组)

一种处理所有嵌入一个通用结构的结构类型的方法(json编组)

问题描述:

I have a gin-gonic web app with somewhat MVC architecture. I created a couple of models, all of them embed one common struct:

type User struct {
   ID int
   Name string
}

type Admin struct {
   User
   Level int
}

... {
   User
}

Now I want to store them in database in json format. The goal I want to accomplish is to code only one function/method that will marshal any model and will save save it into DB. This method must marshal all the fields of current model, not only from User struct, e.g. User must be marshaled into {id: 1, name: "zhora"}, while Admin will go into {id: 1, name: "gena", level: 2}.

Like this one:

func (i *User) Save() {
  data, err := json.Marshal(i)
  check(err)
  if i.ID == 0 {
    _, err = app.DB.Exec(`INSERT INTO users(data) VALUES ($1) `, string(data))
  } else {
    _, err = app.DB.Exec(`UPDATE users SET data = $1 WHERE id=$2`, string(data), i.ID)
  }
  check(err)
}

Right now I have to copy/paste this func to every model file, changing only method receiver. Can this be avoided?

我有一个具有MVC架构的gin-gonic网络应用。 我创建了几个模型,所有模型都嵌入了一个通用结构: p>

  type用户结构{
 ID int 
名称 字符串
} 
 
键入管理结构{
用户
级别int 
} 
 
 ... {
用户
} 
  code>  pre> 
 
  {id:1,名称:“ zhora”}  code>,而Admin将进入 {id:1,name:“ gena”,级别:2}  code  >。 p> 
 
 

像这样的一个: p>

  func(i * User)Save()  {
 data,err:= json.Marshal(i)
 check(err)
如果i.ID == 0 {
 _,err = app.DB.Exec(`INSERT INTO users(data)VALUES  ($ 1)`,string(data))
} else {
 _,err = app.DB.Exec(`UPDATE users SET data = $ 1 WHERE id = $ 2`,string(data),i.ID)\  n} 
 check(err)
} 
  code>  pre> 
 
 

现在我必须将此 func code>复制/粘贴到每个模型文件中, 仅更改方法接收者。 可以避免吗? p> div>

You may use one func Save(d interface{}) like this:

package main

import (
    "encoding/json"
    "fmt"
)

type User struct {
    ID   int
    Name string
}

type Admin struct {
    User
    Level int
}

func main() {
    Save(User{})
    Save(Admin{})
}

func Save(d interface{}) {
    body, err := json.Marshal(d)
    if err != nil {
        panic(err)
    }
    st := string(body)
    fmt.Println(st)
}

output:

{"ID":0,"Name":""}
{"ID":0,"Name":"","Level":0}

for your case, use this one function for all types:

func Save(i interface{}, id int) {
    data, err := json.Marshal(i)
    check(err)
    if id == 0 {
        _, err = app.DB.Exec(`INSERT INTO users(data) VALUES ($1) `, string(data))
    } else {
        _, err = app.DB.Exec(`UPDATE users SET data = $1 WHERE id=$2`, string(data), id)
    }
    check(err)
}

And call it like this:

u := User{}
a := Admin{}

Save(u, u.ID)
Save(a, a.ID)

And Yes, this is even simplifies call to Save to one parameter:

package main

import (
    "encoding/json"
    "fmt"
)

type Model interface {
    ID() int
    setID(int)
}

type User struct {
    Id   int
    Name string
}

func (t User) ID() int      { return t.Id }
func (t User) setID(id int) { t.Id = id }

type Admin struct {
    User
    Level int
}

func main() {
    Save(User{})
    Save(Admin{})
}

func Save(d Model) {
    body, err := json.Marshal(d)
    if err != nil {
        panic(err)
    }
    st := string(body)
    fmt.Println(st)

    fmt.Println("ID is ", d.ID())
}

output:

{"Id":0,"Name":""}
ID is  0
{"Id":0,"Name":"","Level":0}
ID is  0

Now You may use this one function for all types:

func Save(i Model) {
    data, err := json.Marshal(i)
    check(err)
    id := i.ID()
    if id == 0 {
        _, err = app.DB.Exec(`INSERT INTO users(data) VALUES ($1) `, string(data))
    } else {
        _, err = app.DB.Exec(`UPDATE users SET data = $1 WHERE id=$2`, string(data), id)
    }
    check(err)
}

And call it like this:

u := User{}
a := Admin{}

Save(u)
Save(a)

Effective Go:

Getters

Go doesn't provide automatic support for getters and setters. There's nothing wrong with providing getters and setters yourself, and it's often appropriate to do so, but it's neither idiomatic nor necessary to put Get into the getter's name. If you have a field called owner (lower case, unexported), the getter method should be called Owner (upper case, exported), not GetOwner. The use of upper-case names for export provides the hook to discriminate the field from the method. A setter function, if needed, will likely be called SetOwner. Both names read well in practice:

owner := obj.Owner()
if owner != user {
    obj.SetOwner(user)
}