Игнорировать конкретные узлы / атрибуты при сравнении двух 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")))