使用 golang 的 json.Unmarshal,将字符串反序列化到对象结构时,若字段原先有值,而被反序列化字符串不包含该字段值,则无法清空对象字段值。
举个例子:
func TestUnmarshal(t *testing.T) {
stu := &Student{Age: 11}
err := json.Unmarshal([]byte(`{"Name": "Lilian"}`), stu)
if err != nil {
t.Error(err)
return
}
t.Logf("stu:%+v", *stu)
}
测试结果:(Age 字段保留了原值)
TestUnmarshal: config_test.go:64: stu:{Name:Lilian Age:11}
原因:json Unmarshal 的时候只会更新对应的字段值,字符串未包含 Age 的字段,因此,Age 字段不会被更新。
这种效果在某些情况下是符合预期的,但有些情况,并不是我们希望的结果。例如,在动态同步远程配置的场景。
业务代码自动同步远程配置中心下发的配置变更,将变更的字符串信息 Unmarshal 到目标对象上。当删除配置时,若直接 Unmarshal 到原对象,则无法清空删除配置的字段值。
这么常见的问题,大家一定有好的解决方案吧,搜了一下,竟没找到比较好的方案,故简单记录下自己的解决方案,希望有所帮助。
// 根据jsonRaw更新target对象,无论target是否有值,一律清空
func FullUpdate(jsonRaw string, target interface{}) error {
// 定义一个新对象
newTargetValue := reflect.New(reflect.TypeOf(target).Elem())
newTarget := newTargetValue.Interface()
// 反序列化到新对象上
err := json.Unmarshal([]byte(jsonRaw), newTarget)
if err != nil {
return err
}
// 重置目标对象
reflect.ValueOf(target).Elem().Set(newTargetValue.Elem())
return nil
}
测试代码:
func TestUnmarshal(t *testing.T) {
stu := Student{Age: 11}
str := `{"Name": "Lilian"}`
FullUpdate(str, &stu)
t.Logf("stu:%+v", commutil.ToJsonString(stu))
}
测试结果:(Age 字段被清空)
TestUnmarshal: config_test.go:59: stu:{"Name":"Lilian","Age":0}
之所以需要使用 reflect,是因为 target 对象可能已经在其他地方使用了,必须保证该指针指向不变,仅改变指向对象的内容。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。