go-cmp の cmp.Diff() でJSON文字列を比較できるようにする

go-cmp を使うと2つの構造体のDiffを取ることができるが、プロパティにJSON文字列([]bytejson.RawMessage など)があると、内容は同じでもインデント等が異なるとdiffが出てしまい、うまく比較できない。

type Hoge struct {
    ID   string
    JSON json.RawMessage
}

func main() {
a := Hoge{ID: "1", JSON: json.RawMessage(`{"a":1, "b":2}`)}
b := Hoge{ID: "1", JSON: json.RawMessage(`{"b":2, "a":1}`)}
diff := cmp.Diff(a, b)
fmt.Println(diff) // diff がでる

この場合は、cmp.Transformer()JSON文字列をjson.Unmarshal()して構造体に変換するようにして、cmp.Diff()のオプションに渡すことで比較できるようになる1

func CmpTransformJSON() cmp.Option {
    return cmp.FilterValues(
        func(x, y []byte) bool {
            return json.Valid(x) && json.Valid(y)
        },
        cmp.Transformer("ParseJSON", func(in []byte) (out interface{}) {
            if err := json.Unmarshal(in, &out); err != nil {
                panic(err)
            }
            return out
        }),
    )
}
type Hoge struct {
    ID   string
    JSON json.RawMessage
}

a := Hoge{ID: "1", JSON: json.RawMessage(`{"a":1, "b":2}`)}
b := Hoge{ID: "1", JSON: json.RawMessage(`{"b":2, "a":1}`)}
diff := cmp.Diff(a, b, CmpTransformJSON())
fmt.Println(diff) // diff がでない

まとめると以下のようになる。

package main

import (
    "encoding/json"
    "fmt"

    "github.com/google/go-cmp/cmp"
)

type Hoge struct {
    ID   string
    JSON json.RawMessage
}

func main() {
    a := Hoge{ID: "1", JSON: json.RawMessage(`{"a":1, "b":2}`)}
    b := Hoge{ID: "1", JSON: json.RawMessage(`{"b":2, "a":1}`)}
    {
        diff := cmp.Diff(a, b)
        fmt.Println("diff:", diff) // diff がでる
    }
    {
        diff := cmp.Diff(a, b, CmpTransformJSON())
        fmt.Println("diff:", diff) // diff がでない
    }
}

func CmpTransformJSON() cmp.Option {
    return cmp.FilterValues(
        func(x, y []byte) bool {
            return json.Valid(x) && json.Valid(y)
        },
        cmp.Transformer("ParseJSON", func(in []byte) (out interface{}) {
            if err := json.Unmarshal(in, &out); err != nil {
                panic(err)
            }
            return out
        }),
    )
}

Go Playground - The Go Programming Language

環境

  • Go: 1.9.3
  • go-cmp: 0.5.9