2015-09-05 6 views
2

Краткая история: Как я могу сравнить два куска JSON? Ниже приведен код ошибки.Как сравнить два запроса 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 
} 

Длинная история: Я делаю некоторые E2e тестирование и часть этого мне нужно сравнить запрашиваемую JSON тело с полученным JSON. Для этого я попытался развязать ожидаемый и полученный json с пустым интерфейсом (во избежание ошибок какого-либо типа), но я получаю сообщение об ошибке: json: Unmarshal(nil). Я предполагаю, что кодировка/json не нравится пустой интерфейс, поэтому вопрос заключается в том, как сравнить два куска JSON? Сравнение строк будет подвержено ошибкам, поэтому я пытаюсь избежать этого.

+1

* Я думаю * вам просто нужно изменить свое заявление на 'J, J2: = карта [строка] интерфейс {} {}, отображение [ строка] интерфейс {} {} '. Я * знаю *, вы хотите, чтобы в одиночном примере был наилучший шанс на хороший ответ на SO. :) – twotwotwo

+1

Нет, мой первоначальный комментарий был неправильным; это то, что вам нужно передать указатели в 'Decode' /' Unmarshal' (все примеры по-прежнему помогают!). – twotwotwo

ответ

3

Необходимо направить указатели на Decode и Unmarshal. Я выставил runnable sample с func JSONEqual(a, b io.Reader) и JSONBytesEqual(a, b []byte), оба возвращались (bool, error). Вы можете сравнить тело запроса с вашим статическим ожидаемым контентом (например, вы пытаетесь сделать в вопросе), обернув ожидаемое содержимое с помощью bytes.NewBuffer или strings.NewReader. Вот код:

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

Он выводит:

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 
+0

Я читал, что нет смысла указывать указатель на интерфейс, поскольку интерфейс {} уже является своего рода указателем. Почему это другое? –

+0

Хороший вопрос, и если вы прочтете, что на StackOverflow это может быть моя ошибка, и, возможно, я должен пересмотреть этот ответ, хе-хе. Интерфейсы, такие как значения срезов, действительно небольшие структуры, содержащие указатели. Но здесь мы действительно хотим позволить 'Unmarshal' * изменить то, что находится в этой маленькой структуре * (от' nil' до интерфейса 'map [struct] {}' ему придется выделять), поэтому мы должны дать ему указатель на него. – twotwotwo

+0

Возможно, это больше, чем это, но значение интерфейса является указательным, когда вы пытаетесь избежать копирования: если оно относится к большому фрагменту данных, копирование значения интерфейса не будет копировать указанные данные. Так, например, [b = a' здесь] (http://play.golang.org/p/Hs6-_3bAi5) не выделяет 1000 байтов. Вам все еще нужно использовать указатели, когда предмет, на который указывает, изменяется, поэтому вам здесь нужен. – twotwotwo

Смежные вопросы