为了账号安全,请及时绑定邮箱和手机立即绑定

如何使用 r3labs/diff 区分整个结构

如何使用 r3labs/diff 区分整个结构

Go
ABOUTYOU 2022-10-17 10:05:57
我遇到了github.com/r3labs/diffGo 语言库来比较相同类型的两个结构。库运行良好,除了一个用例如下:我使用Date结构来表示日期:type Date struct {    Year  int    Month int    Day   int}现在,还有一些其他更复杂的结构可以使用该Date结构,例如:type Student struct {  DateOfBirth Date}如果我要比较两个学生,比如diff.Diff(  Student{DateOfBirth: Date{2021, 11, 13}},  Student{DateOfBirth: Date{2021, 10, 9}},)结果我会得到一个包含 2 个项目的更改日志,一个DateOfBirth > Month用于DateOfBirth > Day.我想要的结果将是一个带有单个项目 ( DateOfBirth) 和2021-10-09.图书馆有可能吗?
查看完整描述

2 回答

?
翻阅古今

TA贡献1780条经验 获得超5个赞

注意此解决方案正在使用github.com/r3labs/diff/v2.

没有这样的选择。diff 只是递归地处理结构字段并为每个不同的字段生成更改日志。

要实现您想要的输出,您可以实现自己的ValueDiffer. 这样,您可以“原子地”区分结构并以您想要的格式附加到变更日志。

一个人为的例子,部分从包内部复制:

type DateDiffer struct {

}


// Whether this differ should be used to match a specific type

func (d *DateDiffer) Match(a, b reflect.Value) bool {

    return diff.AreType(a, b, reflect.TypeOf(Date{}))

}


// The actual diff function, where you also append to the changelog

// using your custom format

func (d *DateDiffer) Diff(cl *diff.Changelog, path []string, a, b reflect.Value) error {

    if a.Kind() == reflect.Invalid {

        cl.Add(diff.CREATE, path, nil, b.Interface())

        return nil

    }

    if b.Kind() == reflect.Invalid {

        cl.Add(diff.DELETE, path, a.Interface(), nil)

        return nil

    }

    var d1, d2 Date

    d1, _ = a.Interface().(Date)

    d2, _ = b.Interface().(Date)

    if d1.Day != d2.Day || d1.Month != d2.Month || d1.Year != d2.Year {

        cl.Add(diff.UPDATE, path, fmt.Sprintf("%d-%d-%d", d1.Year, d1.Month, d1.Day), fmt.Sprintf("%d-%d-%d", d2.Year, d2.Month, d2.Day))

    }

    return nil

}


// unsure what this is actually for, but you must implement it either way

func (d *DateDiffer) InsertParentDiffer(dfunc func(path []string, a, b reflect.Value, p interface{}) error) {

    return

}

然后你这样使用它:


    d2, _ := diff.NewDiffer(diff.CustomValueDiffers(&DateDiffer{}))


    s1 := Student{DateOfBirth: Date{2021, 11, 13}}

    s2 := Student{DateOfBirth: Date{2021, 10, 9}}


    ch2, _ := d2.Diff(s1, s2)

输出(编组和缩进的 json):


[

  {

   "type": "update",

   "path": [

    "DateOfBirth"

   ],

   "from": "2021-11-13",

   "to": "2021-10-9"

  }

 ]


查看完整回答
反对 回复 2022-10-17
?
喵喔喔

TA贡献1735条经验 获得超5个赞

经过一番研究,我找到了解决方案。


我需要为 the 创建一个自定义差异,Date并使用DisableStructValues包中的选项。


此选项很有用,因为它禁止为结构中的每个项目填充单独的更改,并在将其与nil值进行比较时返回整个对象。


diff.Diff(

  Student{DateOfBirth: Date{2021, 11, 13}},

  Student{DateOfBirth: Date{2021, 10, 9}},

  diff.CustomValueDiffers(differ.DateDiffer{}),

  diff.DisableStructValues()

)

要实现自定义差异,需要一个实现以下接口的新结构:


type ValueDiffer interface {

    Match(a, b reflect.Value) bool

    Diff(cl *Changelog, path []string, a, b reflect.Value) error

    InsertParentDiffer(dfunc func(path []string, a, b reflect.Value, p interface{}) error)

}

这是我的自定义不同的实现。


type DateDiffer struct {

    DiffFunc (func(path []string, a, b reflect.Value, p interface{}) error)

}


func (differ DateDiffer) Match(a, b reflect.Value) bool {

    return diff.AreType(a, b, reflect.TypeOf(Date{}))

}


func (differ DateDiffer) Diff(cl *diff.Changelog, path []string, a, b reflect.Value) error {

    if a.Kind() == reflect.Invalid {

        cl.Add(diff.CREATE, path, nil, b.Interface())

        return nil

    }


    if b.Kind() == reflect.Invalid {

        cl.Add(diff.DELETE, path, a.Interface(), nil)

        return nil

    }


    var source, target Date

    source, _ = a.Interface().(Date)

    target, _ = b.Interface().(Date)

    if !source.Equal(target) {

        cl.Add(diff.UPDATE, path, a.Interface(), b.Interface())

    }


    return nil

}


func (differ DateDiffer) InsertParentDiffer(dfunc func(path []string, a, b reflect.Value, p interface{}) error) {

    differ.DiffFunc = dfunc

}

希望这会帮助有类似用例的人。


查看完整回答
反对 回复 2022-10-17
  • 2 回答
  • 0 关注
  • 113 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信