JSON 由于 ObjectMapper,补丁比较失败?
JSON Patch diffing fails due to ObjectMapper?
我有 2 个对象要比较。为此,我在 spring-boot 版本 2.1.3 中使用 Jackson ObjectMapper
将它们反序列化为字符串,将它们作为树读取(转换为 JsonNode
),然后将它们区分为创建一个 JsonPatch
对象。我注意到的是,在现有对象的 JsonNode 中,所有具有 null
值的字段都被跳过,就像只有 null
值字段的对象一样。 ObjectMapper
似乎在消费者(spring-引导版本 2.6.7)和服务提供者(spring-boot-2.1.3)中表现不同。示例,在树中:
"_optionalAttrs":{"styles":{"99_0002_4_24_002":{"hineck":null,"choices":{"99_0002_4_24_002_001":{"color":null}}},"99_0002_4_24_001":{"hineck":null,"choices":null}}.
对
"_optionalAttrs":{"styles":{"99_0002_4_24_002":{"choices":{"99_0002_4_24_002_001":{}}},"99_0002_4_24_001":{}}
我猜这就是生成的 JsonPatch 操作不正确的原因:
op: copy; from: "/_optionalAttrs/styles/99_0002_4_24_001/hineck"; path: "/_optionalAttrs/clientAttributes/channel"
我最终收到此错误 - 目标 JSON 文档中没有这样的路径。有没有办法确保两者保持一致?如果您认为还有其他问题,请告诉我。消费者代码是我们可以更改的东西,但服务提供者代码不属于我们所有。我正在使用 json-patch-1.12
和 jdk 11
.
我没有很好的解决方案,但总比没有好
您可以使用 fieldAndValueMap 方法将对象映射到 Map 并比较它们
public Map<String, String> fieldAndValueMap(Class<?> type, Object obj) throws ReflectiveOperationException {
Map<String, String> result = new HashMap<>();
for (Field field : type.getDeclaredFields()) {
field.setAccessible(true);
if (isWrapperType(field.getType())) {
String value = nonNull(field.get(obj)) ? field.get(obj).toString() : "null";
result.put(type.getSimpleName() + "." + field.getName(), value);
} else {
String getMethod = "get" + Character.toUpperCase(field.getName().charAt(0)) + field.getName().substring(1);
Method method = type.getMethod(getMethod);
Object subObj = method.invoke(obj);
if (nonNull(subObj)) {
result.putAll(fieldAndValueMap(field.getType(), subObj));
}
}
}
return result;
}
public boolean isWrapperType(Class<?> type) {
return getWrapperTypes().contains(type);
}
private Set<Class<?>> getWrapperTypes() {
Set<Class<?>> ret = new HashSet<>();
ret.add(Boolean.class);
ret.add(Character.class);
ret.add(String.class);
ret.add(Byte.class);
ret.add(Short.class);
ret.add(Integer.class);
ret.add(Long.class);
ret.add(Float.class);
ret.add(Double.class);
ret.add(Void.class);
ret.add(BigDecimal.class);
ret.add(LocalDate.class);
ret.add(LocalDateTime.class);
ret.add(LocalTime.class);
ret.add(Date.class);
return ret;
}
获取对象作为字段和值的映射:fieldAndValueMap(firstObject.getClass(), firstObject)
默认情况下,ObjectMapper
将保留 null
个值,但您可以使用以下设置跳过它们:
objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL)
我有 2 个对象要比较。为此,我在 spring-boot 版本 2.1.3 中使用 Jackson ObjectMapper
将它们反序列化为字符串,将它们作为树读取(转换为 JsonNode
),然后将它们区分为创建一个 JsonPatch
对象。我注意到的是,在现有对象的 JsonNode 中,所有具有 null
值的字段都被跳过,就像只有 null
值字段的对象一样。 ObjectMapper
似乎在消费者(spring-引导版本 2.6.7)和服务提供者(spring-boot-2.1.3)中表现不同。示例,在树中:
"_optionalAttrs":{"styles":{"99_0002_4_24_002":{"hineck":null,"choices":{"99_0002_4_24_002_001":{"color":null}}},"99_0002_4_24_001":{"hineck":null,"choices":null}}.
对
"_optionalAttrs":{"styles":{"99_0002_4_24_002":{"choices":{"99_0002_4_24_002_001":{}}},"99_0002_4_24_001":{}}
我猜这就是生成的 JsonPatch 操作不正确的原因:
op: copy; from: "/_optionalAttrs/styles/99_0002_4_24_001/hineck"; path: "/_optionalAttrs/clientAttributes/channel"
我最终收到此错误 - 目标 JSON 文档中没有这样的路径。有没有办法确保两者保持一致?如果您认为还有其他问题,请告诉我。消费者代码是我们可以更改的东西,但服务提供者代码不属于我们所有。我正在使用 json-patch-1.12
和 jdk 11
.
我没有很好的解决方案,但总比没有好
您可以使用 fieldAndValueMap 方法将对象映射到 Map
public Map<String, String> fieldAndValueMap(Class<?> type, Object obj) throws ReflectiveOperationException {
Map<String, String> result = new HashMap<>();
for (Field field : type.getDeclaredFields()) {
field.setAccessible(true);
if (isWrapperType(field.getType())) {
String value = nonNull(field.get(obj)) ? field.get(obj).toString() : "null";
result.put(type.getSimpleName() + "." + field.getName(), value);
} else {
String getMethod = "get" + Character.toUpperCase(field.getName().charAt(0)) + field.getName().substring(1);
Method method = type.getMethod(getMethod);
Object subObj = method.invoke(obj);
if (nonNull(subObj)) {
result.putAll(fieldAndValueMap(field.getType(), subObj));
}
}
}
return result;
}
public boolean isWrapperType(Class<?> type) {
return getWrapperTypes().contains(type);
}
private Set<Class<?>> getWrapperTypes() {
Set<Class<?>> ret = new HashSet<>();
ret.add(Boolean.class);
ret.add(Character.class);
ret.add(String.class);
ret.add(Byte.class);
ret.add(Short.class);
ret.add(Integer.class);
ret.add(Long.class);
ret.add(Float.class);
ret.add(Double.class);
ret.add(Void.class);
ret.add(BigDecimal.class);
ret.add(LocalDate.class);
ret.add(LocalDateTime.class);
ret.add(LocalTime.class);
ret.add(Date.class);
return ret;
}
默认情况下,ObjectMapper
将保留 null
个值,但您可以使用以下设置跳过它们:
objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL)