Тестирование двух объектов JSON на равенство, игнорируя дочерний порядок в Java - PullRequest
206 голосов
/ 12 февраля 2010

Я ищу библиотеку синтаксического анализа JSON, которая поддерживает сравнение двух объектов JSON, игнорирующих дочерний порядок, особенно для модульного тестирования JSON, возвращаемого из веб-службы.

Поддерживает ли это какая-либо из основных библиотек JSON? Библиотека org.json просто выполняет сравнение ссылок.

Ответы [ 22 ]

136 голосов
/ 06 апреля 2012

Попробуйте Skyscreamer JSONAssert .

Режим не строгий имеет два основных преимущества, которые делают его менее хрупким:

  • Расширяемость объекта (например, с ожидаемым значением {id: 1} , это все равно будет проходить: {id: 1, moredata: 'x'} .)
  • Свободный порядок массива (например, ['dog', 'cat'] == ['cat', 'dog'])

В строгом режиме он больше похож на тестовый класс json-lib.

Тест выглядит примерно так:

@Test
public void testGetFriends() {
    JSONObject data = getRESTData("/friends/367.json");
    String expected = "{friends:[{id:123,name:\"Corby Page\"}"
        + ",{id:456,name:\"Solomon Duskis\"}]}";
    JSONAssert.assertEquals(expected, data, false);
}

Параметры в вызове JSONAssert.assertEquals (): ОжидаемыйJSONString , actualDataString и isStrict .

Полученные сообщения довольно ясны, что важно при сравнении действительно больших объектов JSON.

73 голосов
/ 13 февраля 2010

Как общий архитектурный аспект, я обычно советую не допускать, чтобы зависимости от конкретного формата сериализации выходили за пределы вашего уровня хранения / сети; таким образом, я бы сначала порекомендовал вам рассмотреть возможность проверки равенства между собственными объектами приложения, а не их проявлениями JSON.

Сказав это, я в настоящее время большой поклонник Джексона , который, как я быстро прочитал об их реализации ObjectNode.equals () , позволяет выполнить сравнение набора членов, которое вы хотите:

public boolean equals(Object o)
{
    if (o == this) return true;
    if (o == null) return false;
    if (o.getClass() != getClass()) {
        return false;
    }
    ObjectNode other = (ObjectNode) o;
    if (other.size() != size()) {
        return false;
    }
    if (_children != null) {
        for (Map.Entry<String, JsonNode> en : _children.entrySet()) {
            String key = en.getKey();
            JsonNode value = en.getValue();

            JsonNode otherValue = other.get(key);

            if (otherValue == null || !otherValue.equals(value)) {
                return false;
            }
        }
    }
    return true;
}
40 голосов
/ 30 января 2012

Использование GSON

JsonParser parser = new JsonParser();
JsonElement o1 = parser.parse("{a : {a : 2}, b : 2}");
JsonElement o2 = parser.parse("{b : 2, a : {a : 2}}");
assertEquals(o1, o2);
24 голосов
/ 11 июня 2014

Я бы сделал следующее,

JSONObject obj1 = /*json*/;
JSONObject obj2 = /*json*/;

ObjectMapper mapper = new ObjectMapper();

JsonNode tree1 = mapper.readTree(obj1.toString());
JsonNode tree2 = mapper.readTree(obj2.toString());

return tree1.equals(tree2);
13 голосов
/ 23 мая 2011

Вы можете попробовать использовать json-lib JSONAssert class:

JSONAssert.assertEquals(
  "{foo: 'bar', baz: 'qux'}",
  JSONObject.fromObject("{foo: 'bar', baz: 'xyzzy'}")
);

Дает:

junit.framework.ComparisonFailure: objects differed at key [baz]; expected:<[qux]> but was:<[xyzzy]>
13 голосов
/ 02 ноября 2014

Использовать эту библиотеку: https://github.com/lukas-krecan/JsonUnit

Pom:

<dependency>
    <groupId>net.javacrumbs.json-unit</groupId>
    <artifactId>json-unit</artifactId>
    <version>1.5.0</version>
    <scope>test</scope>
</dependency>

IGNORING_ARRAY_ORDER - игнорирует порядок в массивах

assertJsonEquals("{\"test\":[1,2,3]}",
  "{\"test\":  [3,2,1]}",
  when(IGNORING_ARRAY_ORDER)
);
11 голосов
/ 11 мая 2013

Если вы уже используете JUnit, в последней версии теперь используется Hamcrest. Это общая структура сопоставления (особенно полезная для модульного тестирования), которую можно расширить для создания новых сопоставителей.

Существует небольшая библиотека с открытым исходным кодом, которая называется hamcrest-json с соответствиями в формате JSON. Это хорошо документировано, протестировано и поддерживается. Ниже приведены несколько полезных ссылок:

Пример кода с использованием объектов из библиотеки JSON org.json.simple:

Assert.assertThat(
    jsonObject1.toJSONString(),
    SameJSONAs.sameJSONAs(jsonObject2.toJSONString()));

При желании вы можете (1) разрешить массивы "любого порядка" и (2) игнорировать дополнительные поля.

Поскольку для Java существует множество библиотек JSON (Jackson, GSON, json-lib и т. Д.), Полезно, чтобы hamcrest-json поддерживал текст JSON (как java.lang.String), а также изначально поддерживаемые объекты из библиотеки JSON Дугласа Крокфорда org.json.

Наконец, если вы не используете JUnit, вы можете использовать Hamcrest напрямую для утверждений. ( Я писал об этом здесь. )

11 голосов
/ 10 февраля 2012

Вы можете попробовать JsonUnit . Он может сравнивать два объекта JSON и сообщать о различиях. Он построен на вершине Джексона.

Например

assertJsonEquals("{\"test\":1}", "{\n\"test\": 2\n}");

Результаты в

java.lang.AssertionError: JSON documents are different:
Different value found in node "test". Expected 1, got 2.
6 голосов
/ 09 ноября 2011

Одна вещь, которую я сделал, и она творит чудеса, это прочитать оба объекта в HashMap, а затем сравнить с обычным assertEquals ().Он вызовет метод equals () хеш-карт, который будет рекурсивно сравнивать все объекты внутри (это будут либо другие хеш-карты, либо какой-либо объект с одним значением, например строка или целое число).Это было сделано с использованием синтаксического анализатора JSON Codehaus.

assertEquals(mapper.readValue(expectedJson, new TypeReference<HashMap<String, Object>>(){}), mapper.readValue(actualJson, new TypeReference<HashMap<String, Object>>(){}));

Аналогичный подход можно использовать, если вместо этого объект JSON является массивом.

4 голосов
/ 20 июля 2012

Для org.json я развернул свое собственное решение, метод, который сравнивается с экземплярами JSONObject. Я не работал со сложными объектами JSON в этом проекте, поэтому я не знаю, работает ли это во всех сценариях. Кроме того, учитывая, что я использую это в модульных тестах, я не прикладывал усилий к оптимизации. Вот оно:

public static boolean jsonObjsAreEqual (JSONObject js1, JSONObject js2) throws JSONException {
    if (js1 == null || js2 == null) {
        return (js1 == js2);
    }

    List<String> l1 =  Arrays.asList(JSONObject.getNames(js1));
    Collections.sort(l1);
    List<String> l2 =  Arrays.asList(JSONObject.getNames(js2));
    Collections.sort(l2);
    if (!l1.equals(l2)) {
        return false;
    }
    for (String key : l1) {
        Object val1 = js1.get(key);
        Object val2 = js2.get(key);
        if (val1 instanceof JSONObject) {
            if (!(val2 instanceof JSONObject)) {
                return false;
            }
            if (!jsonObjsAreEqual((JSONObject)val1, (JSONObject)val2)) {
                return false;
            }
        }

        if (val1 == null) {
            if (val2 != null) {
                return false;
            }
        }  else if (!val1.equals(val2)) {
            return false;
        }
    }
    return true;
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...