如何比较两个JSON请求?

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.

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

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/ecampolo/gomparator/blob/master/internal/platform/json/json.go

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))