使用@JsonFormat注解可以自定义将Java对象转换为JSON字符串时的日期格式,但是在使用过程中也存在一个坑点。具体攻略如下:
1.问题描述
我们在使用@JsonFormat注解时,想要将日期格式化为类似"yyyy-MM-dd HH:mm:ss.SSS"的字符串格式,于是我们在实体类上添加该注解:
public class User {
private Long id;
private String name;
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss.SSS", timezone = "Asia/Shanghai")
private Date createTime;
//getters/setters...
}
在将该实体类转换为JSON字符串时,我们期望得到的日期格式应该是"2021-08-31 15:30:40.123"这样的结果。但是实际上转换后却是"2021-09-01T07:30:40.123+0000"这样的结果,时间格式和时区都并不是我们设置的内容。
2.问题分析
JsonFormat注解中的"timezone"属性,其实是将java中的Date对象按照指定的timezone转为标准时间字符串,然后再转换成JSON字符串。因此,问题就在于@JsonFormat注解没有将我们设置的时间格式直接应用到Date对象上,因为各个时区的时间格式可能是不同的。
3.解决方案
要解决这个问题,我们需要在转换JSON字符串时,指定jackson的序列化配置。具体来说,就是通过自定义ObjectMapper,设置其默认的输出时间格式和时区。以下是两种实现方式:
方式一:编写ObjectMapperProvider
编写ObjectMapperProvider,设置默认的输出时间格式和时区:
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder;
import org.springframework.http.converter.json.Jackson2ObjectMapperFactoryBean;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.TimeZone;
public class CustomObjectMapperProvider {
private static final Logger log = LoggerFactory.getLogger(CustomObjectMapperProvider.class);
public ObjectMapper createObjectMapper() {
DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
dateFormat.setTimeZone(TimeZone.getTimeZone("Asia/Shanghai"));
ObjectMapper objectMapper = new Jackson2ObjectMapperBuilder().build();
objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
objectMapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
objectMapper.setTimeZone(TimeZone.getTimeZone("Asia/Shanghai"));
objectMapper.setDateFormat(dateFormat);
// Register JavaTimeModule
objectMapper.registerModule(new JavaTimeModule());
// Fix Jackson2DateFormat issue (https://github.com/FasterXML/jackson-databind/issues/1072)
objectMapper.findAndRegisterModules();
return objectMapper;
}
}
在上面的例子中,我们使用了SimpleDateFormat指定了默认输出的时间格式,同时也设置了时区。我们通过Jackson2ObjectMapperBuilder来构建ObjectMapper对象,然后通过ObjectMapper的方法设置它的序列化配置。最后,在ObjectMapper中注册JavaTimeModule(解决LocalDateTime转换的问题)。
方式二:编写自定义序列化器
除了通过ObjectMapper的方式来解决这个问题之外,我们还可以编写自定义序列化器,将Java中的Data对象按照我们预期的方式进行序列化。以下是示例代码:
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializerProvider;
import org.springframework.boot.jackson.JsonComponent;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.TimeZone;
@JsonComponent
public class JsonDateSerializer extends JsonSerializer<Date> {
private static final SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
static {
dateFormat.setTimeZone(TimeZone.getTimeZone("Asia/Shanghai"));
}
@Override
public void serialize(Date value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
gen.writeString(dateFormat.format(value));
}
}
在上面的代码中,我们使用@JsonComponent注解将自定义的序列化器注册到Spring Boot框架中。同时,我们也在该序列化器中使用了SimpleDateFormat指定了默认输出的时间格式,同时也设置了时区。注意,这个方法里必须手动写代码将日期转为字符串进行输出。
4.总结
当使用@JsonFormat时,无法直接实现对Java对象的日期格式化。应该使用自定义ObjectMapper或者自定义序列化器的方式,对日期格式和时区进行配置,才能正确地将Java对象转换为JSON字符串。