ObjectMapper 将类转换为映射,我遇到了麻烦。 我有一个 util 类。像这样。
@Slf4j
public class JsonUtil {
private static final ObjectMapper objectMapper;
static {
objectMapper = new ObjectMapper();
JavaTimeModule javaTimeModule = new JavaTimeModule();
javaTimeModule.addDeserializer(LocalDateTime.class, new LocalDateTimeDeserializer(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
javaTimeModule.addSerializer(LocalDateTime.class, new LocalDateTimeSerializer(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
objectMapper.registerModule(javaTimeModule);
objectMapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
}
public static Map<String, Object> toMap(Object obj) {
try {
return objectMapper.convertValue(obj, new TypeReference<>() {
});
} catch (Exception e) {
log.error("failed:", e);
return Map.of();
}
}
public static Map<String, Object> toMapIgnoreNull(Object obj) {
try {
return objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL)
.convertValue(obj, new TypeReference<>() {
});
} catch (Exception e) {
log.error("failed:", e);
return Map.of();
}
}
}
toMapIgnoreNull
方法似乎将对象转换为映射,同时忽略空值。它使用 Jackson 的 ObjectMapper
来实现此目的。以下是其作用的详细说明:
它将序列化包含设置为
NON_NULL
,这意味着空值不会包含在生成的 JSON 中。
它使用
convertValue
的 ObjectMapper
方法将给定对象转换为地图。
如果在此过程中发生任何异常,它会捕获异常,记录错误消息,并返回一个空映射。
这个
toMap
方法也用于将对象转换为映射,但它不会忽略空值。它还利用了 Jackson 库中的 ObjectMapper
。下面是该方法的详细解释:
它使用
convertValue
的 ObjectMapper
方法将给定对象转换为地图。
如果在此过程中发生任何异常,它会捕获异常,记录错误消息,并返回一个空地图。
与前一种方法相比,此方法不会过滤掉 null 值,而是保留对象中的所有键值对,即使该值为 null。
什么时候
@Data
@EqualsAndHashCode(callSuper = false)
static class User {
private String username;
private String password;
}
public static void johnOut() {
User john = new User();
john.setUsername("John");
Map<String, Object> johnMap = toMap(john);
System.out.println("result 1: " + johnMap);
Map<String, Object> JohnIgnoreNullMap = toMapIgnoreNull(john);
System.out.println("result 2: " + JohnIgnoreNullMap);
}
private static void jimOut() {
User jim = new User();
jim.setUsername("jim");
Map<String, Object> jimIgnoreNullMap = toMapIgnoreNull(jim);
System.out.println("result 3: " + jimIgnoreNullMap);
Map<String, Object> jimMap = toMap(jim);
System.out.println("result 4: " + jimMap);
}
public static void main(String[] args) {
johnOut();
jimOut();
}
when `johnOut()` is in front, the console prints:
result 1: {username=John, password=null}
result 2: {username=John, password=null}
result 3: {username=jim, password=null}
result 4: {username=jim, password=null}
but when `jimOut()` is in front, the console prints:
result 3: {username=jim}
result 4: {username=jim}
result 1: {username=John}
result 2: {username=John}
why?
expectation:
result 1: {username=John, password=null}
result 2: {username=John, password=null}
result 3: {username=jim}
result 4: {username=jim}
or
result 3: {username=jim}
result 4: {username=jim}
result 1: {username=John, password=null}
result 2: {username=John, password=null}
Is there any experience that can help me?
为了确保每个方法调用独立运行而不影响彼此的设置,需要在每个方法调用内创建一个新的ObjectMapper实例。这样,每个方法都有自己的 ObjectMapper 实例和自己的设置,并且在一个方法中所做的更改不会影响另一个方法的行为。
这里是类似问题的有用答案:https://stackoverflow.com/a/37409580/12403892
您看到此行为的原因是您的
ObjectMapper
类中设置 JsonUtil
的方式。 ObjectMapper
实例设置为静态变量,这意味着它在 toMap
和 toMapIgnoreNull
方法的所有调用之间共享。
setSerializationInclusion(JsonInclude.Include.NON_NULL)
函数会更改 ObjectMapper
的状态 - 一旦调用它,使用相同 ObjectMapper
完成的所有后续序列化也将忽略空值,除非您专门重置它。
您在
johnOut()
和 jimOut()
中看到不同的结果,因为执行顺序决定了 ObjectMapper
是否设置为排除空值。
这是您的
JsonUtil
课程的改进版本:
@Slf4j
public class JsonUtil {
private static final ObjectMapper objectMapper;
static {
objectMapper = new ObjectMapper();
JavaTimeModule javaTimeModule = new JavaTimeModule();
javaTimeModule.addDeserializer(LocalDateTime.class, new LocalDateTimeDeserializer(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
javaTimeModule.addSerializer(LocalDateTime.class, new LocalDateTimeSerializer(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
objectMapper.registerModule(javaTimeModule);
objectMapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
}
public static Map<String, Object> toMap(Object obj) {
try {
return objectMapper.convertValue(obj, new TypeReference<>() {});
} catch (Exception e) {
log.error("failed:", e);
return Map.of();
}
}
public static Map<String, Object> toMapIgnoreNull(Object obj) {
try {
ObjectMapper localMapper = objectMapper.copy();
localMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
return localMapper.convertValue(obj, new TypeReference<>() {});
} catch (Exception e) {
log.error("failed:", e);
return Map.of();
}
}
}
在
toMapIgnoreNull
方法中,我创建了 ObjectMapper
的本地副本,以防止它影响 toMapIgnoreNull
或 toMap
的任何其他调用。现在,您的 johnOut()
和 jimOut()
方法可以按任意顺序排列,并且它们应该根据所使用的方法产生可预测的结果。