Mergo未按预期用Source覆盖Destination问题(类型不一致场景)
使用mergo合并JSON时类型不匹配导致覆盖失效的问题
问题重现
尝试用mergo合并两个JSON,要求Source的值覆盖Destination的值,但当字段类型不匹配(一个是字符串,一个是哈希表)时,合并逻辑未按预期执行:
代码片段
package main import ( "encoding/json" "fmt" "github.com/imdario/mergo" ) var ( a = `{"a": "60"}` b = `{"a": { "name":"asprin", "dose":0.12,"stt":true} }` ) func main() { var source, destination map[string]interface{} err := json.Unmarshal([]byte(a), &destination) if err != nil { panic(err) } err = json.Unmarshal([]byte(b), &source) if err != nil { panic(err) } fmt.Println(destination, source) err = mergo.MergeWithOverwrite(&destination, &source, mergo.WithOverride, mergo.WithTypeCheck) if err != nil { panic(err) } fmt.Println(&destination, " <-- result") }
当前输出
&map[a:60] <-- result which is value of destination. Mergo is not changing anything
期望输出
要么成功覆盖:
map[a:map[dose:0.12 name:asprin stt:true]] <--result
要么抛出类型不匹配的错误。
问题原因
mergo的WithTypeCheck选项会在字段类型不匹配时跳过该字段的合并操作,既不覆盖也不报错。结合WithOverride使用时,仅当类型完全一致时才会执行覆盖,这就导致类型不同的字段保留了Destination的原有值。
解决方案
方案1:强制覆盖(忽略类型差异)
如果需求是不管类型是否匹配,都用Source的值覆盖Destination,只需移除mergo.WithTypeCheck选项:
package main import ( "encoding/json" "fmt" "github.com/imdario/mergo" ) var ( a = `{"a": "60"}` b = `{"a": { "name":"asprin", "dose":0.12,"stt":true} }` ) func main() { var source, destination map[string]interface{} err := json.Unmarshal([]byte(a), &destination) if err != nil { panic(err) } err = json.Unmarshal([]byte(b), &source) if err != nil { panic(err) } fmt.Println(destination, source) // 移除WithTypeCheck,强制用Source值覆盖 err = mergo.MergeWithOverwrite(&destination, &source, mergo.WithOverride) if err != nil { panic(err) } fmt.Println(destination, " <-- result") }
运行输出:
map[a:60] map[a:map[dose:0.12 name:asprin stt:true]] map[a:map[dose:0.12 name:asprin stt:true]] <-- result
方案2:类型不匹配时抛出错误
如果需要在类型不匹配时明确报错而非静默跳过,可以在合并前手动检查字段类型:
package main import ( "encoding/json" "fmt" "reflect" "github.com/imdario/mergo" ) var ( a = `{"a": "60"}` b = `{"a": { "name":"asprin", "dose":0.12,"stt":true} }` ) // 检查Source与Destination的同名字段类型是否匹配 func checkTypeMatch(dest, src map[string]interface{}) error { for key := range src { destVal, exists := dest[key] if !exists { continue } if reflect.TypeOf(destVal) != reflect.TypeOf(srcVal) { return fmt.Errorf("field %s type mismatch: dest is %T, src is %T", key, destVal, src[key]) } } return nil } func main() { var source, destination map[string]interface{} err := json.Unmarshal([]byte(a), &destination) if err != nil { panic(err) } err = json.Unmarshal([]byte(b), &source) if err != nil { panic(err) } fmt.Println(destination, source) // 先执行类型检查 err = checkTypeMatch(destination, source) if err != nil { panic(err) } // 类型匹配时再执行合并 err = mergo.MergeWithOverwrite(&destination, &source, mergo.WithOverride, mergo.WithTypeCheck) if err != nil { panic(err) } fmt.Println(destination, " <-- result") }
运行输出:
panic: field a type mismatch: dest is string, src is map[string]interface {}
内容的提问来源于stack exchange,提问作者Romil Shah
相关产品推荐
相关产品推荐

