27

Short Story: How can I compare two chunks of JSON? The code below errors out.

var j, j2 interface{} b := []byte(srv.req) if err := json.Unmarshal(b, j); err !=nil{ t.Errorf("err %v, req %s", err, b) return } d := json.NewDecoder(r.Body) if err := d.Decode(j2); err !=nil{ t.Error(err) return } if !reflect.DeepEqual(j2, j){ t.Errorf("j %v, j2 %v", j, j2) return } 

Long Story: I'm doing some E2E testings and part of this I need to compare the requested JSON body with the received JSON. To do this I've tried to unmarshal the expected and received json to an empty interface (to avoid any type mistakes) but I get an error: json: Unmarshal(nil). I guess encoding/json doesn't like the empty interface so the question is how can I compare two chunks of JSON? A string comparison would be error prone so I'm trying to avoid that.

2
  • 1
    I think you just need to change your declaration to j, j2 := map[string]interface{}{}, map[string]interface{}{}. I know you want a self-contained example for the best chance of a good answer on SO, though. :) Commented Sep 5, 2015 at 2:43
  • 1
    Nope, my initial comment was wrong; it's that you need to pass pointers into Decode/Unmarshal (complete examples still help though!). Commented Sep 5, 2015 at 3:01

4 Answers 4

48

Super late to the party here.

There is a popular testing package in golang called require github.com/stretchr/testify/require and it will do this for you.

func TestJsonEquality(t *testing.t) { expected := `{"a": 1, "b": 2} ` actual := ` {"b": 2, "a": 1}` require.JSONEq(t, expected, actual) } 

GoDocs: https://godoc.org/github.com/stretchr/testify/require#JSONEqf

Sign up to request clarification or add additional context in comments.

3 Comments

Also a bit late for the comment here ...Is there a way to exclude some parts from json comparison with JSONEq function ? I cannot find something relevant at the godoc.
This doesn't work when you have lists on JSON objects. If the items are out of order in the lists, the equality is false.
@th3morg, a list is order dependent, so if the order in list mismach then the equality is false.
19

You need to pass pointers to Decode and Unmarshal. I put up a runnable sample with func JSONEqual(a, b io.Reader) and JSONBytesEqual(a, b []byte), both returning (bool, error). You can compare a request body to your static expected content (like you're trying to do in the question) by wrapping your expected content using bytes.NewBuffer or strings.NewReader. Here's the code:

package main import ( "encoding/json" "fmt" "io" "reflect" ) // JSONEqual compares the JSON from two Readers. func JSONEqual(a, b io.Reader) (bool, error) { var j, j2 interface{} d := json.NewDecoder(a) if err := d.Decode(&j); err != nil { return false, err } d = json.NewDecoder(b) if err := d.Decode(&j2); err != nil { return false, err } return reflect.DeepEqual(j2, j), nil } // JSONBytesEqual compares the JSON in two byte slices. func JSONBytesEqual(a, b []byte) (bool, error) { var j, j2 interface{} if err := json.Unmarshal(a, &j); err != nil { return false, err } if err := json.Unmarshal(b, &j2); err != nil { return false, err } return reflect.DeepEqual(j2, j), nil } func main() { a := []byte(`{"x": ["y",42]}`) b := []byte(`{"x": ["y", 42]}`) c := []byte(`{"z": ["y", "42"]}`) empty := []byte{} bad := []byte(`{this? this is a test.}`) eq, err := JSONBytesEqual(a, b) fmt.Println("a=b\t", eq, "with error", err) eq, err = JSONBytesEqual(a, c) fmt.Println("a=c\t", eq, "with error", err) eq, err = JSONBytesEqual(a, empty) fmt.Println("a=empty\t", eq, "with error", err) eq, err = JSONBytesEqual(a, bad) fmt.Println("a=bad\t", eq, "with error", err) } 

It outputs:

a=b true with error <nil> a=c false with error <nil> a=empty false with error EOF a=bad false with error invalid character 't' looking for beginning of object key string 

5 Comments

I read there is no point to have a pointer to an interface as the interface{} is already a kind of pointer. Why is this different?
Good question, and if you read that on StackOverflow it might be my fault and maybe I should revise that answer, heh. Interfaces, like slice values, are really little structures containing pointers. But here, we actually want to let Unmarshal change what's in that little structure (from nil to a map[struct]interface{} it'll have to allocate), so we have to give it a pointer to it.
There is probably more to it than this, but an interface value is pointer-like when you're trying to avoid copies: if it refers to a big chunk of data, copying the interface value won't copy the data pointed to. So, for example, the b=a here doesn't allocate 1000 bytes. You still need to use pointers when the thing pointed to is being modified -- that's why you need one here.
(I just added some detail to the top section at stackoverflow.com/questions/23542989/… to try and avoid confusing any future readers about that.)
Great! Thanks a lot for the answer!
5

I wrote a tool for comparing http json-based responses and I do so ignoring order. You can take a look at the package that implements the comparison and grab the Equal function: https://github.com/emacampolo/gomparator/blob/master/json_util.go#L10

eg:

b1 := []byte(`{"x": {"t": 1, "s": 2}, "z": 1}`) b2 := []byte(`{"z": 1, "x": {"s": 2, "t": 1}}`) j1, _ := Unmarshal(b1) j2, _ := Unmarshal(b2) assert.True(t, Equal(j1, j2)) 

Comments

0

Consider using the package at https://pkg.go.dev/github.com/wI2L/jsondiff that helps computing the diff between two JSON documents as a series of RFC6902 (JSON Patch) operations. An example form the docs:

import "github.com/wI2L/jsondiff" patch, err := jsondiff.Compare(pod, newPod) if err != nil { // handle error } b, err := json.MarshalIndent(patch, "", " ") if err != nil { // handle error } os.Stdout.Write(b) 

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.