系列一、SpringBoot统一格式化日期 & 将long型数字转换成字符串

当数据库中的bigint类型数据通过接口传递给Vue前端时,出现了精度丢失。解决方案是通过在SpringBoot后端配置Jackson转换器,将Long类型序列化为String,以及对日期进行格式化处理,确保数据正确传输。

一、现象描述

        数据库中的字段保存的是bigint类型的数据,前端vue通过接口获取数据时,该字段的值却丢失精度了,如下图所示:

二、解决方案

2.1、导入依赖

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-json</artifactId>
</dependency>

2.2、Constants.java

public class Constants {
    public static String DEFAULT_DATE_TIME_FORMAT = "yyyy-MM-dd HH:mm:ss";
    public static String DEFAULT_DATE_FORMAT = "yyyy-MM-dd";
    public static String DEFAULT_TIME_FORMAT = "HH:mm:ss";
}

2.3、配置转换器

package org.star.config;

import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateDeserializer;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalTimeDeserializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateSerializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalTimeSerializer;
import com.fasterxml.jackson.module.paramnames.ParameterNamesModule;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import org.star.constant.Constants;

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
import java.util.List;


@Configuration
@EnableWebMvc
public class MyWebMvcConfigurer implements WebMvcConfigurer {

    @Override
    public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
        MappingJackson2HttpMessageConverter jackson2HttpMessageConverter =new MappingJackson2HttpMessageConverter();

        /**
         * 序列换成json时,将所有的long变成String,因为js中得数字类型不能包含所有的java long值
         */
        ObjectMapper objectMapper =objectMapper();
        SimpleModule simpleModule =new SimpleModule();
        simpleModule.addSerializer(Long.class, ToStringSerializer.instance);
        simpleModule.addSerializer(Long.TYPE, ToStringSerializer.instance);
        objectMapper.registerModule(simpleModule);
        jackson2HttpMessageConverter.setObjectMapper(objectMapper);
        converters.add(jackson2HttpMessageConverter);

    }

    /**
     * 格式化日期
     * @return
     */
    @Bean
    public ObjectMapper objectMapper() {
        ObjectMapper objectMapper = new ObjectMapper();
        objectMapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
        objectMapper.disable(DeserializationFeature.ADJUST_DATES_TO_CONTEXT_TIME_ZONE);
        JavaTimeModule javaTimeModule = new JavaTimeModule();
        javaTimeModule.addSerializer(LocalDateTime.class,new LocalDateTimeSerializer(DateTimeFormatter.ofPattern(Constants.DEFAULT_DATE_TIME_FORMAT)));
        javaTimeModule.addSerializer(LocalDate.class,new LocalDateSerializer(DateTimeFormatter.ofPattern(Constants.DEFAULT_DATE_FORMAT)));
        javaTimeModule.addSerializer(LocalTime.class,new LocalTimeSerializer(DateTimeFormatter.ofPattern(Constants.DEFAULT_TIME_FORMAT)));
        javaTimeModule.addDeserializer(LocalDateTime.class,new LocalDateTimeDeserializer(DateTimeFormatter.ofPattern(Constants.DEFAULT_DATE_TIME_FORMAT)));
        javaTimeModule.addDeserializer(LocalDate.class,new LocalDateDeserializer(DateTimeFormatter.ofPattern(Constants.DEFAULT_DATE_FORMAT)));
        javaTimeModule.addDeserializer(LocalTime.class,new LocalTimeDeserializer(DateTimeFormatter.ofPattern(Constants.DEFAULT_TIME_FORMAT)));
        objectMapper.registerModule(javaTimeModule).registerModule(new ParameterNamesModule());
        return objectMapper;
    }

}

2.4、效果

2.5、参考

https://www.bilibili.com/read/cv22828897/
https://www.jianshu.com/p/cf50c4bc71d3
https://www.cnblogs.com/exmyth/p/14305299.html(靠谱[推荐])
http://www.shouxicto.com/article/2858.html(靠谱[不推荐])

&lt;think&gt; 我们需要将日期统一序列化为long(时间戳)的形式,同时数字保持原样(即不改变数字的序列化方式)。在Spring Boot中,默认情况下,Jackson会将`java.util.Date`序列化为时间戳(long),但对于Java 8的日期(如`LocalDateTime`)则可能序列化为数组形式。因此,我们需要统一配置,使得所有日期都序列化为long的时间戳(毫秒级或秒级)。 解决方案: 1. 对于传统的`java.util.Date`,默认就是时间戳,不需要额外配置。 2. 对于Java 8的日期,我们需要注册`JavaTimeModule`,并配置`SerializationFeature.WRITE_DATES_AS_TIMESTAMPS`,这样`JavaTimeModule`就会将Java 8日期序列化为时间戳(默认为毫秒级,即自1970-01-01T00:00:00Z以来的毫秒数)。 3. 注意:如果希望使用秒级时间戳,则可以通过自定义序列化器来实现。 具体步骤: - 在`ObjectMapper`中禁用`WRITE_DATES_AS_TIMESTAMPS`会导致日期被序列化为字符串,因此我们需要启用`WRITE_DATES_AS_TIMESTAMPS`(默认是启用的,但为了确保,可以不禁用它,或者显式启用)。 - 注册`JavaTimeModule`以支持Java 8日期的序列化和反序列化。 - 如果需要秒级时间戳,我们可以为特定的日期注册自定义的序列化器,将日期换为秒。 配置示例(毫秒级时间戳): ```java @Configuration public class JacksonConfig { @Bean public ObjectMapper objectMapper() { ObjectMapper mapper = new ObjectMapper(); // 注册Java 8时间模块 JavaTimeModule javaTimeModule = new JavaTimeModule(); mapper.registerModule(javaTimeModule); // 启用将日期作为时间戳(毫秒)序列化 mapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, true); // 注意:对于Java 8日期,在注册了JavaTimeModule后,启用WRITE_DATES_AS_TIMESTAMPS就会将其序列化为时间戳 // 确保日期在反序列化时能正确换时区 mapper.setTimeZone(TimeZone.getDefault()); return mapper; } } ``` 但是,上述配置对于`LocalDateTime`等类会序列化为个数组(例如[2024,6,15,14,30,45,123456789])而不是时间戳,这是因为`JavaTimeModule`默认对`LocalDateTime`的序列化并不是时间戳。因此,我们需要为`LocalDateTime`等类自定义序列化器,将其换为时间戳。 修正方案:为Java 8日期注册自定义的序列化器,将它们换为毫秒或秒的时间戳。 例如,将`LocalDateTime`换为毫秒时间戳(需要先换为`Instant`,通常需要指定时区,因为`LocalDateTime`没有时区信息,所以我们需要指定个时区来换为UTC时间或其他时区的时间戳): ```java javaTimeModule.addSerializer(LocalDateTime.class, new JsonSerializer&lt;LocalDateTime&gt;() { @Override public void serialize(LocalDateTime value, JsonGenerator gen, SerializerProvider serializers) throws IOException { // 将LocalDateTime换为指定时区(比如系统默认时区)的Instant,然后获取毫秒数 long millis = value.atZone(ZoneId.systemDefault()).toInstant().toEpochMilli(); gen.writeNumber(millis); } }); ``` 同样,我们也可以处理`LocalDate`(换为从1970-01-01开始的天数): ```java javaTimeModule.addSerializer(LocalDate.class, new JsonSerializer&lt;LocalDate&gt;() { @Override public void serialize(LocalDate value, JsonGenerator gen, SerializerProvider serializers) throws IOException { // 将LocalDate换为从1970-01-01开始的天数 long days = value.toEpochDay(); gen.writeNumber(days); } }); ``` 完整配置(统一为时间戳,包括毫秒和天): ```java @Configuration public class JacksonConfig { @Bean public ObjectMapper objectMapper() { ObjectMapper mapper = new ObjectMapper(); JavaTimeModule javaTimeModule = new JavaTimeModule(); // 处理LocalDateTime:换为毫秒时间戳 javaTimeModule.addSerializer(LocalDateTime.class, new JsonSerializer&lt;LocalDateTime&gt;() { @Override public void serialize(LocalDateTime value, JsonGenerator gen, SerializerProvider serializers) throws IOException { long millis = value.atZone(ZoneId.systemDefault()).toInstant().toEpochMilli(); gen.writeNumber(millis); } }); // 处理LocalDate:换为天数 javaTimeModule.addSerializer(LocalDate.class, new JsonSerializer&lt;LocalDate&gt;() { @Override public void serialize(LocalDate value, JsonGenerator gen, SerializerProvider serializers) throws IOException { gen.writeNumber(value.toEpochDay()); } }); // 处理ZonedDateTime:直接换为毫秒时间戳(使用自带的InstantSerializer,但这里我们自定义以保持致,也可以使用默认的,但默认会序列化为数组) // 我们也可以使用内置的,但为了统一为时间戳,我们同样换为毫秒 javaTimeModule.addSerializer(ZonedDateTime.class, new JsonSerializer&lt;ZonedDateTime&gt;() { @Override public void serialize(ZonedDateTime value, JsonGenerator gen, SerializerProvider serializers) throws IOException { gen.writeNumber(value.toInstant().toEpochMilli()); } }); // 处理Instant:直接换为毫秒时间戳 javaTimeModule.addSerializer(Instant.class, new JsonSerializer&lt;Instant&gt;() { @Override public void serialize(Instant value, JsonGenerator gen, SerializerProvider serializers) throws IOException { gen.writeNumber(value.toEpochMilli()); } }); // 注册模块 mapper.registerModule(javaTimeModule); // 对于传统的Date,默认就是时间戳,所以不需要额外配置 // 禁用日期序列化为字符串 mapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, true); // 设置时区 mapper.setTimeZone(TimeZone.getDefault()); return mapper; } } ``` 注意:这样配置后,所有的日期都会被序列化为long的时间戳(毫秒或天)。在反序列化时,我们同样需要配置相应的反序列化器,因为默认情况下Jackson可能无法将long的时间戳换为Java 8的日期。 因此,我们还需要添加反序列化器: ```java // 为LocalDateTime添加反序列化器 javaTimeModule.addDeserializer(LocalDateTime.class, new JsonDeserializer&lt;LocalDateTime&gt;() { @Override public LocalDateTime deserialize(JsonParser p, DeserializationContext ctxt) throws IOException { long millis = p.getLongValue(); return LocalDateTime.ofInstant(Instant.ofEpochMilli(millis), ZoneId.systemDefault()); } }); // 为LocalDate添加反序列化器 javaTimeModule.addDeserializer(LocalDate.class, new JsonDeserializer&lt;LocalDate&gt;() { @Override public LocalDate deserialize(JsonParser p, DeserializationContext ctxt) throws IOException { long days = p.getLongValue(); return LocalDate.ofEpochDay(days); } }); // 其他日期的反序列化类似 ``` 完整配置(包括序列化和反序列化): ```java @Configuration public class JacksonConfig { @Bean public ObjectMapper objectMapper() { ObjectMapper mapper = new ObjectMapper(); JavaTimeModule javaTimeModule = new JavaTimeModule(); // 序列化器 javaTimeModule.addSerializer(LocalDateTime.class, new LocalDateTimeSerializer()); javaTimeModule.addSerializer(LocalDate.class, new LocalDateSerializer()); javaTimeModule.addSerializer(ZonedDateTime.class, new ZonedDateTimeSerializer()); javaTimeModule.addSerializer(Instant.class, new InstantSerializer()); // 反序列化器 javaTimeModule.addDeserializer(LocalDateTime.class, new LocalDateTimeDeserializer()); javaTimeModule.addDeserializer(LocalDate.class, new LocalDateDeserializer()); javaTimeModule.addDeserializer(ZonedDateTime.class, new ZonedDateTimeDeserializer()); javaTimeModule.addDeserializer(Instant.class, new InstantDeserializer()); mapper.registerModule(javaTimeModule); mapper.setTimeZone(TimeZone.getDefault()); return mapper; } // 以下为内部类定义序列化器和反序列化器 private static class LocalDateTimeSerializer extends JsonSerializer&lt;LocalDateTime&gt; { @Override public void serialize(LocalDateTime value, JsonGenerator gen, SerializerProvider serializers) throws IOException { long millis = value.atZone(ZoneId.systemDefault()).toInstant().toEpochMilli(); gen.writeNumber(millis); } } private static class LocalDateSerializer extends JsonSerializer&lt;LocalDate&gt; { @Override public void serialize(LocalDate value, JsonGenerator gen, SerializerProvider serializers) throws IOException { gen.writeNumber(value.toEpochDay()); } } private static class ZonedDateTimeSerializer extends JsonSerializer&lt;ZonedDateTime&gt; { @Override public void serialize(ZonedDateTime value, JsonGenerator gen, SerializerProvider serializers) throws IOException { gen.writeNumber(value.toInstant().toEpochMilli()); } } private static class InstantSerializer extends JsonSerializer&lt;Instant&gt; { @Override public void serialize(Instant value, JsonGenerator gen, SerializerProvider serializers) throws IOException { gen.writeNumber(value.toEpochMilli()); } } private static class LocalDateTimeDeserializer extends JsonDeserializer&lt;LocalDateTime&gt; { @Override public LocalDateTime deserialize(JsonParser p, DeserializationContext ctxt) throws IOException { long millis = p.getLongValue(); return LocalDateTime.ofInstant(Instant.ofEpochMilli(millis), ZoneId.systemDefault()); } } private static class LocalDateDeserializer extends JsonDeserializer&lt;LocalDate&gt; { @Override public LocalDate deserialize(JsonParser p, DeserializationContext ctxt) throws IOException { long days = p.getLongValue(); return LocalDate.ofEpochDay(days); } } private static class ZonedDateTimeDeserializer extends JsonDeserializer&lt;ZonedDateTime&gt; { @Override public ZonedDateTime deserialize(JsonParser p, DeserializationContext ctxt) throws IOException { long millis = p.getLongValue(); return ZonedDateTime.ofInstant(Instant.ofEpochMilli(millis), ZoneId.systemDefault()); } } private static class InstantDeserializer extends JsonDeserializer&lt;Instant&gt; { @Override public Instant deserialize(JsonParser p, DeserializationContext ctxt) throws IOException { return Instant.ofEpochMilli(p.getLongValue()); } } } ``` 注意:这里我们为每种类都定义了序列化器和反序列化器。在实际应用中,可以根据需要选择配置哪些类。 另外,对于传统的`java.util.Date`,Jackson默认已经支持时间戳序列化和反序列化,因此不需要额外配置。 这样配置后,所有日期都将被序列化为long的时间戳(毫秒或天),而数字(如Integer、Long、BigDecimal等)将保持原样(即序列化为数字,不会变成字符串)。 测试:创建个Controller返回包含各种日期的对象,检查JSON输出是否为long。 相关问题: 1. 如何将日期统一序列化为秒级时间戳? 2. 在序列化为时间戳时,如何考虑时区的影响? 3. 反序列化时,如何处理不同时区的时间戳? 4. 如果某些接口需要返回格式化日期字符串,而其他接口需要时间戳,如何实现? 注意:根据要求,我们最后需要以指定格式提出相关问题。 &lt;/think&gt;### Spring Boot 中 ObjectMapper 统一日期Long 时间戳配置指南 #### 核心需求分析 当需要将日期统一序列化为 Long时间戳时,需处理: 1. 传统 `java.util.Date` 类 2. Java 8 时间类(`LocalDateTime`, `Instant` 等) 3. 时区换问题 4. 时间精度控制(毫秒/秒) --- ### 、全局日期 Long 配置方案 #### 1. 基础配置类 ```java import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.SerializationFeature; import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import java.time.Instant; import java.time.LocalDateTime; import java.time.ZoneId; import java.util.Date; @Configuration public class TimestampConfig { @Bean public ObjectMapper objectMapper() { ObjectMapper mapper = new ObjectMapper(); // 禁用日期字符串格式,启用时间戳格式 mapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS); mapper.enable(SerializationFeature.WRITE_DATE_TIMESTAMPS_AS_NANOSECONDS); // 注册Java 8时间模块(自定义序列化器) JavaTimeModule javaTimeModule = new JavaTimeModule(); // 配置LocalDateTime序列化为毫秒时间戳 javaTimeModule.addSerializer(LocalDateTime.class, new JsonSerializer&lt;LocalDateTime&gt;() { @Override public void serialize(LocalDateTime value, JsonGenerator gen, SerializerProvider provider) throws IOException { long timestamp = value.atZone(ZoneId.systemDefault()) .toInstant() .toEpochMilli(); gen.writeNumber(timestamp); } }); // 配置Instant序列化为毫秒时间戳 javaTimeModule.addSerializer(Instant.class, new JsonSerializer&lt;Instant&gt;() { @Override public void serialize(Instant value, JsonGenerator gen, SerializerProvider provider) throws IOException { gen.writeNumber(value.toEpochMilli()); } }); mapper.registerModule(javaTimeModule); return mapper; } } ``` #### 2. 支持秒级时间戳 ```java @Bean public ObjectMapper objectMapper() { ObjectMapper mapper = new ObjectMapper(); // 配置秒级时间戳 JavaTimeModule module = new JavaTimeModule(); // LocalDateTime秒级时间戳 module.addSerializer(LocalDateTime.class, new JsonSerializer&lt;LocalDateTime&gt;() { @Override public void serialize(LocalDateTime value, JsonGenerator gen, SerializerProvider provider) throws IOException { long seconds = value.atZone(ZoneId.systemDefault()) .toEpochSecond(); gen.writeNumber(seconds); } }); // Instant秒级时间戳 module.addSerializer(Instant.class, new JsonSerializer&lt;Instant&gt;() { @Override public void serialize(Instant value, JsonGenerator gen, SerializerProvider provider) throws IOException { gen.writeNumber(value.getEpochSecond()); } }); mapper.registerModule(module); return mapper; } ``` --- ### 二、处理多种日期 #### 1. 支持所有日期 Long ```java @Bean public ObjectMapper timestampObjectMapper() { ObjectMapper mapper = new ObjectMapper(); JavaTimeModule module = new JavaTimeModule(); // 处理 java.util.Date module.addSerializer(Date.class, new JsonSerializer&lt;Date&gt;() { @Override public void serialize(Date value, JsonGenerator gen, SerializerProvider provider) throws IOException { gen.writeNumber(value.getTime()); } }); // 处理 java.sql.Timestamp module.addSerializer(java.sql.Timestamp.class, new JsonSerializer&lt;java.sql.Timestamp&gt;() { @Override public void serialize(java.sql.Timestamp value, JsonGenerator gen, SerializerProvider provider) throws IOException { gen.writeNumber(value.getTime()); } }); // 处理 ZonedDateTime module.addSerializer(ZonedDateTime.class, new JsonSerializer&lt;ZonedDateTime&gt;() { @Override public void serialize(ZonedDateTime value, JsonGenerator gen, SerializerProvider provider) throws IOException { gen.writeNumber(value.toInstant().toEpochMilli()); } }); // 处理 OffsetDateTime module.addSerializer(OffsetDateTime.class, new JsonSerializer&lt;OffsetDateTime&gt;() { @Override public void serialize(OffsetDateTime value, JsonGenerator gen, SerializerProvider provider) throws IOException { gen.writeNumber(value.toInstant().toEpochMilli()); } }); mapper.registerModule(module); mapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS); return mapper; } ``` #### 2. 时区统一处理 ```java @Bean public ObjectMapper objectMapper() { ObjectMapper mapper = new ObjectMapper(); // 设置全局时区 mapper.setTimeZone(TimeZone.getTimeZone(&quot;Asia/Shanghai&quot;)); JavaTimeModule module = new JavaTimeModule(); // 所有日期使用系统默认时区 module.addSerializer(LocalDateTime.class, new JsonSerializer&lt;LocalDateTime&gt;() { @Override public void serialize(LocalDateTime value, JsonGenerator gen, SerializerProvider provider) throws IOException { ZoneId zoneId = ZoneId.systemDefault(); long timestamp = value.atZone(zoneId).toInstant().toEpochMilli(); gen.writeNumber(timestamp); } }); mapper.registerModule(module); return mapper; } ``` --- ### 三、反序列化:Long 日期对象 #### 1. 时间戳日期配置 ```java @Bean public ObjectMapper objectMapper() { ObjectMapper mapper = new ObjectMapper(); JavaTimeModule module = new JavaTimeModule(); // LongLocalDateTime module.addDeserializer(LocalDateTime.class, new JsonDeserializer&lt;LocalDateTime&gt;() { @Override public LocalDateTime deserialize(JsonParser p, DeserializationContext ctxt) throws IOException { long timestamp = p.getLongValue(); return LocalDateTime.ofInstant( Instant.ofEpochMilli(timestamp), ZoneId.systemDefault() ); } }); // LongDate module.addDeserializer(Date.class, new JsonDeserializer&lt;Date&gt;() { @Override public Date deserialize(JsonParser p, DeserializationContext ctxt) throws IOException { return new Date(p.getLongValue()); } }); // LongInstant module.addDeserializer(Instant.class, new JsonDeserializer&lt;Instant&gt;() { @Override public Instant deserialize(JsonParser p, DeserializationContext ctxt) throws IOException { return Instant.ofEpochMilli(p.getLongValue()); } }); mapper.registerModule(module); return mapper; } ``` #### 2. 智能时间戳换器 ```java public class SmartTimestampDeserializer extends JsonDeserializer&lt;Object&gt; { private final Class&lt;?&gt; targetType; public SmartTimestampDeserializer(Class&lt;?&gt; targetType) { this.targetType = targetType; } @Override public Object deserialize(JsonParser p, DeserializationContext ctxt) throws IOException { long timestamp = p.getLongValue(); Instant instant = Instant.ofEpochMilli(timestamp); if (targetType == Date.class) { return Date.from(instant); } else if (targetType == LocalDateTime.class) { return LocalDateTime.ofInstant(instant, ZoneId.systemDefault()); } else if (targetType == Instant.class) { return instant; } else if (targetType == ZonedDateTime.class) { return ZonedDateTime.ofInstant(instant, ZoneId.systemDefault()); } throw new IllegalArgumentException(&quot;Unsupported date type: &quot; + targetType); } } // 注册使用 module.addDeserializer(Date.class, new SmartTimestampDeserializer(Date.class)); module.addDeserializer(LocalDateTime.class, new SmartTimestampDeserializer(LocalDateTime.class)); ``` --- ### 四、测试与验证 #### 测试实体类 ```java public class TimestampModel { private Date createTime; private LocalDateTime updateTime; private Instant eventTime; private ZonedDateTime expireTime; // 构造函数、getter和setter } ``` #### 测试控制器 ```java @RestController public class TimestampController { @GetMapping(&quot;/timestamp&quot;) public TimestampModel getTimestampModel() { TimestampModel model = new TimestampModel(); model.setCreateTime(new Date()); model.setUpdateTime(LocalDateTime.now()); model.setEventTime(Instant.now()); model.setExpireTime(ZonedDateTime.now().plusDays(30)); return model; } @PostMapping(&quot;/timestamp&quot;) public ResponseEntity&lt;String&gt; acceptTimestamp(@RequestBody TimestampModel model) { // 验证反序列化结果 System.out.println(&quot;Received date: &quot; + model.getCreateTime()); System.out.println(&quot;Received localDateTime: &quot; + model.getUpdateTime()); return ResponseEntity.ok(&quot;Success&quot;); } } ``` #### 预期 JSON 输出 ```json { &quot;createTime&quot;: 1718433600000, &quot;updateTime&quot;: 1718433600000, &quot;eventTime&quot;: 1718433600000, &quot;expireTime&quot;: 1721025600000 } ``` --- ### 五、高级配置技巧 #### 1. 时间戳精度动态控制 ```java public class PrecisionTimestampSerializer extends JsonSerializer&lt;Object&gt; { private final ChronoUnit precision; public PrecisionTimestampSerializer(ChronoUnit precision) { this.precision = precision; } @Override public void serialize(Object value, JsonGenerator gen, SerializerProvider provider) throws IOException { long timestamp; if (value instanceof TemporalAccessor) { Instant instant; if (value instanceof LocalDateTime) { instant = ((LocalDateTime) value).atZone(ZoneId.systemDefault()).toInstant(); } else if (value instanceof ZonedDateTime) { instant = ((ZonedDateTime) value).toInstant(); } else if (value instanceof Instant) { instant = (Instant) value; } else { throw new IllegalArgumentException(&quot;Unsupported temporal type&quot;); } if (precision == ChronoUnit.MILLIS) { timestamp = instant.toEpochMilli(); } else { timestamp = instant.getEpochSecond(); } } else if (value instanceof Date) { long ms = ((Date) value).getTime(); timestamp = (precision == ChronoUnit.MILLIS) ? ms : ms / 1000; } else { throw new IllegalArgumentException(&quot;Unsupported date type&quot;); } gen.writeNumber(timestamp); } } // 注册使用 module.addSerializer(Date.class, new PrecisionTimestampSerializer(ChronoUnit.SECONDS)); module.addSerializer(LocalDateTime.class, new PrecisionTimestampSerializer(ChronoUnit.MILLIS)); ``` #### 2. 注解驱动配置 ```java // 自定义注解 @Retention(RetentionPolicy.RUNTIME) @JacksonAnnotationsInside @JsonSerialize(using = TimestampSerializer.class) @JsonDeserialize(using = TimestampDeserializer.class) public @interface TimestampFormat { TimeUnit unit() default TimeUnit.MILLISECONDS; } // 在实体类中使用 public class Event { @TimestampFormat(unit = TimeUnit.SECONDS) private LocalDateTime eventTime; @TimestampFormat private Date createTime; } ``` --- ### 六、常见问题解决方案 #### 问题1:时区不致 ```java @Bean public ObjectMapper objectMapper() { ObjectMapper mapper = new ObjectMapper(); mapper.setTimeZone(TimeZone.getTimeZone(&quot;UTC&quot;)); // 统一使用UTC时区 JavaTimeModule module = new JavaTimeModule(); module.addSerializer(LocalDateTime.class, new JsonSerializer&lt;LocalDateTime&gt;() { @Override public void serialize(LocalDateTime value, JsonGenerator gen, SerializerProvider provider) throws IOException { // 强制换为UTC时区 long timestamp = value.atZone(ZoneId.of(&quot;UTC&quot;)).toInstant().toEpochMilli(); gen.writeNumber(timestamp); } }); mapper.registerModule(module); return mapper; } ``` #### 问题2:前端传递字符串时间戳 ```java // 支持字符串格式的时间戳反序列化 module.addDeserializer(LocalDateTime.class, new JsonDeserializer&lt;LocalDateTime&gt;() { @Override public LocalDateTime deserialize(JsonParser p, DeserializationContext ctxt) throws IOException { String value = p.getValueAsString(); try { long timestamp = Long.parseLong(value); return LocalDateTime.ofInstant( Instant.ofEpochMilli(timestamp), ZoneId.systemDefault() ); } catch (NumberFormatException e) { // 尝试解析ISO格式 return LocalDateTime.parse(value, DateTimeFormatter.ISO_DATE_TIME); } } }); ``` #### 问题3:空值处理 ```java @Bean public ObjectMapper objectMapper() { ObjectMapper mapper = new ObjectMapper(); // 允许日期字段为null mapper.disable(SerializationFeature.FAIL_ON_EMPTY_BEANS); // 配置空值处理 mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL); JavaTimeModule module = new JavaTimeModule(); module.addSerializer(LocalDateTime.class, new JsonSerializer&lt;LocalDateTime&gt;() { @Override public void serialize(LocalDateTime value, JsonGenerator gen, SerializerProvider provider) throws IOException { if (value == null) { gen.writeNull(); // 返回null而不是0 } else { long timestamp = value.atZone(ZoneId.systemDefault()) .toInstant() .toEpochMilli(); gen.writeNumber(timestamp); } } }); mapper.registerModule(module); return mapper; } ``` --- ###
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值