You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.10 16:31:18