我有一个接受稀疏更新的JSON REST API,但我提出的模式似乎异常冗长。我是以错误的方式解决这个问题吗?
(假设这是使用内置稀疏更新支持的数据存储。)
func choose(a, b *string) *string {
if a != nil {
return a
}
return b
}
type Model {
Id *string `json:"id"`
Field1 *string `json:"field1"`
Field2 *string `json:"field2"`
Field3 *string `json:"field3"`
...
}
func (m1 Model) Update(m2 Model) (m3 Model) {
m3.Id = choose(m2.Id, m1.Id)
m3.Field1 = choose(m2.Field1, m1.Field1)
m3.Field2 = choose(m2.Field2, m1.Field2)
m3.Field3 = choose(m2.Field3, m1.Field3)
...
return
}
func UpdateController(input Model) error {
previous, _ := store.Get(*input.Id)
updated := previous.Update(input)
return store.Put(updated)
}
理想情况下,我可以这样写UpdateController
:
func UpdateController(input Model) {
previous, _ := store.Get(*input.Id)
updated, _ := structs.Update(previous, input)
return store.Put(updated)
}
(为清楚起见,错误处理已被省略。)
答案 0 :(得分:6)
好吧,如果您愿意使用反射,那么问题就变得非常简单了:
http://play.golang.org/p/dc-OnO1cZ4
func (m1 Model) Update(m2 Model) (m3 Model) {
old := reflect.ValueOf(m1)
new := reflect.ValueOf(m2)
final := reflect.ValueOf(&m3).Elem()
for i := 0; i < old.NumField(); i++ {
if !new.Field(i).IsNil() {
final.Field(i).Set(new.Field(i))
} else {
final.Field(i).Set(old.Field(i))
}
}
return
}
我们reflect.ValueOf(&m3).Elem()
的原因是v3需要设置,请参阅http://blog.golang.org/laws-of-reflection
但基本上,通过使用反射,我们可以循环遍历struct字段,查看更新的字段是否为nil,如果是,则使用旧值。
答案 1 :(得分:3)
如果您不想使用反射,另一个选项是从数据库中检索对象并将其地址传递给JSON解码器。仅更新更新方法的JSON中定义的字段。然后,您可以使用常规方法将更改保存到数据库。这是使用gin和gorm的示例代码。
func TodoUpdate(c *gin.Context) {
var todo Todo
todoId := c.Param("todoId")
DB.First(&todo, todoId)
if err := json.NewDecoder(c.Request.Body).Decode(&todo); err != nil {
log.Fatal("Update error. Error:", err.Error())
}
DB.Save(&todo)
}
因此,例如,如果您的数据库中有{"ID":1,"name":"Do stuff","completed":false}
,并将{"completed":true}
之类的内容发送到您的更新方法(/todos/1
,其中1是todoId
),那么您最终会得到这个:{"ID":1,"name":"Do stuff","completed":true}
。
答案 2 :(得分:2)
您可以使用反射和the reflect
package更新Model
。
以下函数就地更新旧的Model
:
func (old *Model) MergeInPlace(new Model) {
for ii := 0; ii < reflect.TypeOf(old).Elem().NumField(); ii++ {
if x := reflect.ValueOf(&new).Elem().Field(ii); !x.IsNil() {
reflect.ValueOf(old).Elem().Field(ii).Set(x)
}
}
}
您可以通过说x.MergeInPlace(y)
来调用此方法,x
和y
为Model
。调用此函数后,x
将被修改。
&#34;合并&#34;以下,
{
"id":"1",
"field1":"one",
"field2":"two",
"field3":"three"
}
{
"id":"1",
"field3":"THREE"
}
的产率:
{
"id":"1",
"field1":"one",
"field2":"two",
"field3":"THREE"
}
也就是说,它会覆盖旧结构中存在的所有值,忽略&#34; undefined&#34;。
显然,你可以随意支持(或不支持)。
See a complete, working example at the Go Playground.
正常警告(检查生产中返回的错误!)适用。