Игнорировать конкретные узлы / атрибуты при сравнении двух JSON

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

В настоящее время я использую JSONAssert из org.SkyScreamer, чтобы сделать сравнение. Это дает мне хороший вывод на консоль, но не игнорирует никаких атрибутов.

например

java.lang.AssertionError messageHeader.sentTime
expected:null
got:09082016 18:49:41.123

Теперь это происходит динамично и должно игнорироваться. Что-то вроде

JSONAssert.assertEquals(expectedJSONString, actualJSONString,JSONCompareMode, *list of attributes to be ignored*)

Было бы здорово, если бы кто-то предложил решение в JSONAssert. Однако другие способы также приветствуются.

3 ответа

Вы можете использовать настройки для этого. Например, если вам нужно игнорировать атрибут верхнего уровня с именем timestamp, используйте:

JSONAssert.assertEquals(expectedResponseBody, responseBody,
            new CustomComparator(JSONCompareMode.LENIENT,
                new Customization("timestamp", (o1, o2) -> true)));

Также возможно использовать выражения пути, такие как "entry.id". В вашей настройке вы можете использовать любой метод для сравнения двух значений. В приведенном выше примере всегда возвращается значение true, независимо от того, что является ожидаемым и фактическим значением. Вы могли бы делать более сложные вещи там, если вам нужно.

Вы можете использовать JsonUnit. Он обладает функциональностью, которую вы ищете, мы можем игнорировать поля, пути и значения, которые являются нулевыми и т. д. Проверьте это для получения дополнительной информации. Что касается примера, вы можете игнорировать такой путь

assertJsonEquals(
     "{\"root\":{\"test\":1, \"ignored\": 2}}", 
     "{\"root\":{\"test\":1, \"ignored\": 1}}", 
     whenIgnoringPaths("root.ignored")
);

Иногда вам нужно игнорировать определенные значения при сравнении. Можно использовать заполнитель ${json-unit.ignore} следующим образом

assertJsonEquals("{\"test\":\"${json-unit.ignore}\"}",
    "{\n\"test\": {\"object\" : {\"another\" : 1}}}");

Спасибо @dknaus за подробный ответ. Хотя это решение не будет работать в СТРОГО режиме иcheckJsonObjectKeysExpectedInActual код метода необходимо заменить на следующий код [как предлагает @tk-gospodinov]:

 for (String attribute : attributesToIgnore) {
     expected.remove(attribute);
     super.checkJsonObjectKeysExpectedInActual(prefix, expected, actual, result);
  }

Прежде всего, есть открытый вопрос для этого.

В моих тестах я сравниваю JSON от контроллера с реальным объектом с помощью JsonUtil класс для сериализации / десериализации:

public class JsonUtil {
    public static <T> List<T> readValues(String json, Class<T> clazz) {
        ObjectReader reader = getMapper().readerFor(clazz);
        try {
            return reader.<T>readValues(json).readAll();
        } catch (IOException e) {
            throw new IllegalArgumentException("Invalid read array from JSON:\n'" + json + "'", e);
        }
    }

    public static <T> T readValue(String json, Class<T> clazz) {
        try {
            return getMapper().readValue(json, clazz);
        } catch (IOException e) {
            throw new IllegalArgumentException("Invalid read from JSON:\n'" + json + "'", e);
        }
    }

    public static <T> String writeValue(T obj) {
        try {
            return getMapper().writeValueAsString(obj);
        } catch (JsonProcessingException e) {
            throw new IllegalStateException("Invalid write to JSON:\n'" + obj + "'", e);
        }
    }

Чтобы игнорировать конкретное поле объекта, я добавил новый метод:

public static <T> String writeIgnoreProps(T obj, String... ignoreProps) {
    try {
        Map<String, Object> map = getMapper().convertValue(obj, new TypeReference<Map<String, Object>>() {});
        for (String prop : ignoreProps) {
            map.remove(prop);
        }
        return getMapper().writeValueAsString(map);
    } catch (JsonProcessingException e) {
        throw new IllegalStateException("Invalid write to JSON:\n'" + obj + "'", e);
    }
}

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

            mockMvc.perform(get(REST_URL))
            .andExpect(status().isOk())
            .andExpect(content().contentTypeCompatibleWith(MediaType.APPLICATION_JSON))
            .andExpect(content().json(JsonUtil.writeIgnoreProps(USER, "registered")))
Другие вопросы по тегам