如何比较两个JSON请求?

huangapple go评论158阅读模式
英文:

How to compare two JSON requests?

问题

短故事:我该如何比较两个 JSON 块?下面的代码报错了。

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
}

长故事:
我正在进行一些端到端的测试,其中一部分是需要将请求的 JSON 主体与接收到的 JSON 进行比较。为了做到这一点,我尝试将预期的和接收到的 JSON 解组为一个空接口(以避免任何类型错误),但是我遇到了一个错误:json: Unmarshal(nil)。我猜测 encoding/json 不喜欢空接口,所以问题是我该如何比较两个 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.

答案1

得分: 38

你好!以下是翻译好的内容:

非常晚才来参加派对。

在golang中有一个受欢迎的测试包,叫做require github.com/stretchr/testify/require,它可以为你完成这个任务。

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

英文:

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

答案2

得分: 17

你需要将指针传递给DecodeUnmarshal函数。我在这里提供了一个可运行的示例,其中包含func JSONEqual(a, b io.Reader)JSONBytesEqual(a, b []byte)两个函数,它们都返回(bool, error)。你可以通过使用bytes.NewBufferstrings.NewReader来将请求体与静态的预期内容进行比较(就像你在问题中尝试的那样)。以下是代码示例:

package main

import (
	"encoding/json"
	"fmt"
	"io"
	"reflect"
)

// JSONEqual比较两个Reader中的JSON。
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比较两个字节切片中的JSON。
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)
}

它的输出结果为:

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
英文:

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 (
&quot;encoding/json&quot;
&quot;fmt&quot;
&quot;io&quot;
&quot;reflect&quot;
)
// 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(&amp;j); err != nil {
return false, err
}
d = json.NewDecoder(b)
if err := d.Decode(&amp;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, &amp;j); err != nil {
return false, err
}
if err := json.Unmarshal(b, &amp;j2); err != nil {
return false, err
}
return reflect.DeepEqual(j2, j), nil
}
func main() {
a := []byte(`{&quot;x&quot;: [&quot;y&quot;,42]}`)
b := []byte(`{&quot;x&quot;:                  [&quot;y&quot;,  42]}`)
c := []byte(`{&quot;z&quot;: [&quot;y&quot;, &quot;42&quot;]}`)
empty := []byte{}
bad := []byte(`{this? this is a test.}`)
eq, err := JSONBytesEqual(a, b)
fmt.Println(&quot;a=b\t&quot;, eq, &quot;with error&quot;, err)
eq, err = JSONBytesEqual(a, c)
fmt.Println(&quot;a=c\t&quot;, eq, &quot;with error&quot;, err)
eq, err = JSONBytesEqual(a, empty)
fmt.Println(&quot;a=empty\t&quot;, eq, &quot;with error&quot;, err)
eq, err = JSONBytesEqual(a, bad)
fmt.Println(&quot;a=bad\t&quot;, eq, &quot;with error&quot;, err)
}

It outputs:

a=b	 true with error &lt;nil&gt;
a=c	 false with error &lt;nil&gt;
a=empty	 false with error EOF
a=bad	 false with error invalid character &#39;t&#39; looking for beginning of object key string

答案3

得分: 4

我为比较基于HTTP的JSON响应编写了一个工具,而且在比较时忽略了顺序。你可以查看实现比较并获取Equal函数的包:
https://github.com/emacampolo/gomparator/blob/master/json_util.go#L10

例如:

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))
英文:

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(`{&quot;x&quot;: {&quot;t&quot;: 1, &quot;s&quot;: 2}, &quot;z&quot;: 1}`)
b2 := []byte(`{&quot;z&quot;: 1, &quot;x&quot;: {&quot;s&quot;: 2, &quot;t&quot;: 1}}`)

j1, _ := Unmarshal(b1)
j2, _ := Unmarshal(b2)
assert.True(t, Equal(j1, j2))

答案4

得分: 0

考虑使用位于https://pkg.go.dev/github.com/wI2L/jsondiff的包,该包可以帮助计算两个JSON文档之间的差异,以一系列RFC6902(JSON Patch)操作的形式呈现。以下是文档中的一个示例:

import "github.com/wI2L/jsondiff"

patch, err := jsondiff.Compare(pod, newPod)
if err != nil {
    // 处理错误
}
b, err := json.MarshalIndent(patch, "", "    ")
if err != nil {
    // 处理错误
}
os.Stdout.Write(b)

这段代码使用jsondiff.Compare函数比较podnewPod两个JSON文档,并返回一个表示差异的patch对象。然后,使用json.MarshalIndent函数将patch对象转换为格式化的JSON字符串,并将其写入标准输出。

英文:

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 &quot;github.com/wI2L/jsondiff&quot;
patch, err := jsondiff.Compare(pod, newPod)
if err != nil {
// handle error
}
b, err := json.MarshalIndent(patch, &quot;&quot;, &quot;    &quot;)
if err != nil {
// handle error
}
os.Stdout.Write(b)

huangapple
  • 本文由 发表于 2015年9月5日 10:15:59
  • 转载请务必保留本文链接:https://go.coder-hub.com/32408890.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定