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

如何在Golang中对比YAML转成的两个Map并导出file2的差异?

YAML结构体对比:解决interface{}类型的列表差异对比问题

我有两个YAML文件,已反序列化为结构体中的Map变量,格式示例如下:

file1.yaml

a: b
c: d
e:
  - f
  - g
x:
  - y

file2.yaml

m: n
c: d
x:
  - y
  - z

我的目标是对比这两个Map,仅输出file2.yaml中的差异。但当前编写的代码无法处理值为列表(slice)类型的情况——因为值的类型是interface{},直接用==或!=无法正确对比嵌套结构。现有代码如下:

type ParametersYaml struct {
    Parameters Params `yaml:"parameters"`
}

type Params map[string]interface{}

var pParam ParametersYaml
var sParam ParametersYaml

func Extractor(primaryYaml, secondaryYaml string) error {
    primaryOpenedFile, err := fileOpener(primaryYaml)
    secondaryOpenedFile, err := fileOpener(secondaryYaml)

    err = yamlParser(primaryOpenedFile, &pParam)
    err = yamlParser(secondaryOpenedFile, &sParam)

    if err != nil {
        return err
    }
    
    compare(pParam, sParam)

    return err

}

func fileOpener(file string) ([]byte, error) {
    f, err := ioutil.ReadFile(file)

    return f, err
}

func yamlParser(fileObject []byte, param *ParametersYaml) error {
    err := yaml.Unmarshal(fileObject, &param)
    return err
}

func compare(pMarshalled, sMarshalled ParametersYaml) {
    for s, sv := range sMarshalled.Parameters {
        for p, pv := range pMarshalled.Parameters {
            if s == p && sv == pv {
                break
            } else if s == p && sv != pv {
                fmt.Printf("%s: %s,\n", s, sv)
                break
            } else if _, ok := pMarshalled.Parameters[s]; !ok {
                fmt.Printf("%s: %s,\n", s, sv)
                break
            }
        }
    }
}

解决方案:递归处理不同类型的值对比

要解决这个问题,需要针对interface{}的实际类型做分支处理,区分slice、map和基础类型,通过递归逻辑对比嵌套结构。修改后的代码如下:

核心对比逻辑修改

// 修改compare函数,直接接收Map参数
func compare(pParams, sParams Params) {
    // 遍历secondary的所有键值对
    for key, sVal := range sParams {
        pVal, exists := pParams[key]
        if !exists {
            // primary中无此键,直接输出secondary的内容
            fmt.Printf("%s: %v\n", key, sVal)
            continue
        }

        // 调用递归函数判断值是否相等
        if !valuesEqual(pVal, sVal) {
            fmt.Printf("%s: %v\n", key, sVal)
        }
    }
}

// valuesEqual 递归对比两个interface{}类型的值是否完全相等
func valuesEqual(a, b interface{}) bool {
    // 类型不一致直接返回不等
    if reflect.TypeOf(a) != reflect.TypeOf(b) {
        return false
    }

    switch aVal := a.(type) {
    case []interface{}:
        // 处理slice类型:先比长度,再逐个元素递归对比
        bVal := b.([]interface{})
        if len(aVal) != len(bVal) {
            return false
        }
        for i := range aVal {
            if !valuesEqual(aVal[i], bVal[i]) {
                return false
            }
        }
        return true
    case map[string]interface{}:
        // 处理嵌套map类型:先比键数量,再逐个键值递归对比
        bVal := b.(map[string]interface{})
        if len(aVal) != len(bVal) {
            return false
        }
        for k, v := range aVal {
            if !valuesEqual(v, bVal[k]) {
                return false
            }
        }
        return true
    default:
        // 基础类型直接用==判断
        return a == b
    }
}

调整Extractor函数的调用

将原有的compare(pParam, sParam)改为直接传入Map参数:

compare(pParam.Parameters, sParam.Parameters)

关键说明

  1. 类型分支处理:通过类型断言区分slice、map和基础类型,避免直接对比interface{}的局限性。
  2. 递归对比:针对嵌套的slice或map,递归调用对比函数,确保深层结构也能准确判断是否相等。
  3. 效率优化:原代码的双层遍历效率低下,改为直接通过键名从primary中查找对应值,逻辑更简洁高效。

针对示例中的YAML文件,运行后会输出预期的差异内容:

m: n
x: [y z]

内容的提问来源于stack exchange,提问作者popopanda

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.03 01:55:34