英文:
Compare two json responses without values with Jackson
问题
这是否可能使用Jackson库比较两个JSON响应,忽略所有值。
因此,以下将断言属性和值。
{
"employee":
{
"id": "1212",
"fullName": "John Miles",
"age": 34
}
}
代码:
ObjectMapper mapper = new ObjectMapper();
assertEquals(mapper.readTree(s1), mapper.readTree(s2));
英文:
Is that possible to compare two json responses with Jackson lib ignoring all values.
So below will assert both attribute and values.
{
"employee":
{
"id": "1212",
"fullName": "John Miles",
"age": 34
}
}
Code:
ObjectMapper mapper = new ObjectMapper();
assertEquals(mapper.readTree(s1), mapper.readTree(s2));
答案1
得分: 1
以下是翻译好的内容:
一个快速查找没有产生任何结果,说明 Jackson 没有内置此功能的能力。然而,我们可以为这个任务编写自己的检查器,以比较两个 Map<String, ?>
,并以这样的方式返回 true
当且仅当:
- 两个
Map
的keySet()
相等。 - 那些是两个
Map
的instanceof Map
的对象的keySet()
相等。 - 那些是
Map
值的值也必须在结构上相等(递归调用)。
对于分析,我们必须假设如果一个 Map
是另一个 Map
中的值,它必须是 Map<String, ?>
,因为我们的解决方案将在此假设下进行未检查的转换。
在这个假设的基础上,解决方案可能如下所示:
@SuppressWarnings("unhecked")
public static boolean areStructuralEqual(Map<String, ?> left, Map<String, ?> right) {
if (!Objects.equals(left.keySet(), right.keySet())) {
return false;
}
Set<String> leftKeysWithMapValues = extractAllKeysThatMapToMaps(left);
Set<String> rightKeysWithMapValues = extractAllKeysThatMapToMaps(right);
if (!Objects.equals(leftKeysWithMapValues, rightKeysWithMapValues)) {
return false;
}
for (String key : leftKeysWithMapValues) {
if (!areStructuralEqual(
(Map<String, ?>) left.get(key),
(Map<String, ?>) right.get(key))) {
return false;
}
}
return true;
}
private static Set<String> extractAllKeysThatMapToMaps(Map<String, ?> map) {
return map.entrySet().stream()
.filter(e -> e.getValue() instanceof Map)
.map(Entry::getKey)
.collect(Collectors.toUnmodifiableSet());
}
请注意,此解决方案不会验证键的类型是否相同。此外,如果一个 Map
的值在另一个 Map
中设置为 null
,而在另一个 Map
中不存在该键,则比较将返回 false
。后者可以通过过滤值为 null
的键来修复:
@SuppressWarnings("unhecked")
public static boolean areStructuralEqual(Map<String, ?> left, Map<String, ?> right) {
Set<Entry<String, ?>> leftEntriesWithNonNullValues =
extractEntriesWithNonNullValues(left);
Set<Entry<String, ?>> rightEntriesWithNonNullValues =
extractEntriesWithNonNullValues(right);
Set<String> leftKeysWithNonNullValues = leftEntriesWithNonNullValues.stream()
.map(Entry::getKey)
.collect(Collectors.toUnmodifiableSet());
Set<String> rightKeysWithNonNullValues = rightEntriesWithNonNullValues.stream()
.map(Entry::getKey)
.collect(Collectors.toUnmodifiableSet());
if (!Objects.equals(leftKeysWithNonNullValues, rightKeysWithNonNullValues)) {
return false;
}
Set<String> leftKeysWithMapValues =
extractAllKeysThatMapToMaps(leftEntriesWithNonNullValues);
Set<String> rightKeysWithMapValues =
extractAllKeysThatMapToMaps(rightEntriesWithNonNullValues);
if (!Objects.equals(leftKeysWithMapValues, rightKeysWithMapValues)) {
return false;
}
for (String key : leftKeysWithMapValues) {
if (!areStructuralEqual(
(Map<String, ?>) left.get(key),
(Map<String, ?>) right.get(key))) {
return false;
}
}
return true;
}
private static Set<String> extractAllKeysThatMapToMaps(Set<Entry<String, ?>> entrySet) {
return entrySet.stream()
.filter(e -> e.getValue() instanceof Map)
.map(Entry::getKey)
.collect(Collectors.toUnmodifiableSet());
}
private static Set<Entry<String, ?>> extractEntriesWithNonNullValues(Map<String, ?> map) {
return map.entrySet().stream()
.filter(e -> Objects.nonNull(e.getValue()))
.collect(Collectors.toUnmodifiableSet());
}
请注意,此解决方案不会验证键的类型是否相同。此外,如果一个 Map
的值在另一个 Map
中设置为 null
,而在另一个 Map
中不存在该键,则比较将返回 false
。后者可以通过过滤值为 null
的键来修复:
@SuppressWarnings("unhecked")
public static boolean areStructuralEqual(Map<String, ?> left, Map<String, ?> right) {
Set<Entry<String, ?>> leftEntriesWithNonNullValues =
extractEntriesWithNonNullValues(left);
Set<Entry<String, ?>> rightEntriesWithNonNullValues =
extractEntriesWithNonNullValues(right);
Set<String> leftKeysWithNonNullValues = leftEntriesWithNonNullValues.stream()
.map(Entry::getKey)
.collect(Collectors.toUnmodifiableSet());
Set<String> rightKeysWithNonNullValues = rightEntriesWithNonNullValues.stream()
.map(Entry::getKey)
.collect(Collectors.toUnmodifiableSet());
if (!Objects.equals(leftKeysWithNonNullValues, rightKeysWithNonNullValues)) {
return false;
}
Set<String> leftKeysWithMapValues =
extractAllKeysThatMapToMaps(leftEntriesWithNonNullValues);
Set<String> rightKeysWithMapValues =
extractAllKeysThatMapToMaps(rightEntriesWithNonNullValues);
if (!Objects.equals(leftKeysWithMapValues, rightKeysWithMapValues)) {
return false;
}
for (String key : leftKeysWithMapValues) {
if (!areStructuralEqual(
(Map<String, ?>) left.get(key),
(Map<String, ?>) right.get(key))) {
return false;
}
}
return true;
}
private static Set<String> extractAllKeysThatMapToMaps(Set<Entry<String, ?>> entrySet) {
return entrySet.stream()
.filter(e -> e.getValue() instanceof Map)
.map(Entry::getKey)
.collect(Collectors.toUnmodifiableSet());
}
private static Set<Entry<String, ?>> extractEntriesWithNonNullValues(Map<String, ?> map) {
return map.entrySet().stream()
.filter(e -> Objects.nonNull(e.getValue()))
.collect(Collectors.toUnmodifiableSet());
}
英文:
A quick lookup did not yield any results that Jackson has built-in capabilites for this functionality. We can, however, write our own checker for this task to compare two Map<String, ?>
s in such a way that we return true
iff.:
- the
keySet()
of bothMap
s are equal - the
keySet()
of those objects that areinstanceof Map
of bothMap
s are equal - the values which are
Map
s must be structurally equally aswell (recursive call)
For the analysis, we have to make the assumption that if a Map
is a value in another Map
, it must be a Map<String, ?>
since our solution will make an unchecked cast with this assumption.
With this assumption, a solution may look like this:
@SuppressWarnings("unhecked")
public static boolean areStructuralEqual(Map<String, ?> left, Map<String, ?> right) {
if (!Objects.equals(left.keySet(), right.keySet())) {
return false;
}
Set<String> leftKeysWithMapValues = extractAllKeysThatMapToMaps(left);
Set<String> rightKeysWithMapValues = extractAllKeysThatMapToMaps(right);
if (!Objects.equals(leftKeysWithMapValues, rightKeysWithMapValues)) {
return false;
}
for (String key : leftKeysWithMapValues) {
if (!areStructuralEqual(
(Map<String, ?>) left.get(key),
(Map<String, ?>) right.get(key))) {
return false;
}
}
return true;
}
private static Set<String> extractAllKeysThatMapToMaps(Map<String, ?> map) {
return map.entrySet().stream()
.filter(e -> e.getValue() instanceof Map)
.map(Entry::getKey)
.collect(Collectors.toUnmodifiableSet());
}
Notice that this solution does not verify that the types of the keys are the same. Also, if the value of a key is set to null
in one Map
and the key is not present in the other Map
, the comparison will return false
. The latter can be fixed by filtering out keys whose values are null
:
@SuppressWarnings("unhecked")
public static boolean areStructuralEqual(Map<String, ?> left, Map<String, ?> right) {
Set<Entry<String, ?>> leftEntriesWithNonNullValues =
extractEntriesWithNonNullValues(left);
Set<Entry<String, ?>> rightEntriesWithNonNullValues =
extractEntriesWithNonNullValues(right);
Set<String> leftKeysWithNonNullValues = leftEntriesWithNonNullValues.stream()
.map(Entry::getKey)
.collect(Collectors.toUnmodifiableSet());
Set<String> rightKeysWithNonNullValues = rightEntriesWithNonNullValues.stream()
.map(Entry::getKey)
.collect(Collectors.toUnmodifiableSet());
if (!Objects.equals(leftKeysWithNonNullValues, rightKeysWithNonNullValues)) {
return false;
}
Set<String> leftKeysWithMapValues =
extractAllKeysThatMapToMaps(leftEntriesWithNonNullValues);
Set<String> rightKeysWithMapValues =
extractAllKeysThatMapToMaps(rightEntriesWithNonNullValues);
if (!Objects.equals(leftKeysWithMapValues, rightKeysWithMapValues)) {
return false;
}
for (String key : leftKeysWithMapValues) {
if (!areStructuralEqual(
(Map<String, ?>) left.get(key),
(Map<String, ?>) right.get(key))) {
return false;
}
}
return true;
}
private static Set<String> extractAllKeysThatMapToMaps(Set<Entry<String, ?>> entrySet) {
return entrySet.stream()
.filter(e -> e.getValue() instanceof Map)
.map(Entry::getKey)
.collect(Collectors.toUnmodifiableSet());
}
private static Set<Entry<String, ?>> extractEntriesWithNonNullValues(Map<String, ?> map) {
return map.entrySet().stream()
.filter(e -> Objects.nonNull(e.getValue()))
.collect(Collectors.toUnmodifiableSet());
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论