英文:
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 bothMaps are equal - the
keySet()of those objects that areinstanceof Mapof bothMaps are equal - the values which are
Maps 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());
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。


评论