Java Spring REST API中Jackson与Gson无法解析时间戳格式startDate的问题排查与解决
问题分析与解决方案
嘿,这个问题我之前也碰到过!核心原因是你的JSON里startDate是毫秒级的数值型时间戳,但DTO中对应的字段类型(比如Date/LocalDateTime)和Jackson/Gson的默认反序列化规则不兼容,导致解析失败。Gson抛出的ParseException就是因为它默认无法自动将纯数字映射为日期类型,而Jackson也需要明确配置才能识别这种格式。
可能的原因拆解
- 你的DTO中
startDate字段定义为java.util.Date、java.time.LocalDateTime等日期类型,但Jackson/Gson默认期望日期是字符串格式(比如"2021-06-04T10:20:00"),而非纯数字时间戳。 - 如果DTO中
startDate是String类型,那JSON里的数值和字符串类型不匹配,自然会解析失败——不过你说设为null就能正常转换,所以大概率是日期类型的适配问题。
针对Jackson的解决方法
方法1:字段级注解配置
直接在DTO的startDate字段上添加@JsonFormat注解,明确告诉Jackson该字段用数值(时间戳)格式反序列化:
import com.fasterxml.jackson.annotation.JsonFormat; import java.util.Date; public class NestedDTO { // 明确指定用数值(毫秒时间戳)解析 @JsonFormat(shape = JsonFormat.Shape.NUMBER) private Date startDate; // getter、setter省略 }
方法2:适配Java 8+时间API(LocalDateTime等)
如果你的DTO用的是java.time.LocalDateTime这类新日期类型,需要额外两步:
- 先添加Jackson的Java 8时间模块依赖(Maven示例):
<dependency> <groupId>com.fasterxml.jackson.datatype</groupId> <artifactId>jackson-datatype-jsr310</artifactId> <version>你的Jackson版本</version> </dependency>
- 配置
ObjectMapper注册模块,并关闭纳秒时间戳解析(因为你的时间戳是毫秒级):
import com.fasterxml.jackson.databind.DeserializationFeature; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; ObjectMapper mapper = new ObjectMapper(); // 注册Java 8时间模块 mapper.registerModule(new JavaTimeModule()); // 禁用纳秒级时间戳解析,适配毫秒级数值 mapper.configure(DeserializationFeature.READ_DATE_TIMESTAMPS_AS_NANOSECONDS, false); MyDTOClass evData = mapper.readValue(evDataJsonStr, MyDTOClass.class); // 注意:这里建议用readValue而非convertValue,convertValue更适合对象转对象,JSON字符串转对象用readValue更稳妥
针对Gson的解决方法
Gson默认不支持将数字直接映射为日期类型,需要自定义TypeAdapter来处理:
方法1:自定义Date类型适配器
import com.google.gson.TypeAdapter; import com.google.gson.stream.JsonReader; import com.google.gson.stream.JsonToken; import com.google.gson.stream.JsonWriter; import java.io.IOException; import java.util.Date; public class DateTimestampAdapter extends TypeAdapter<Date> { @Override public void write(JsonWriter out, Date value) throws IOException { if (value == null) { out.nullValue(); return; } out.value(value.getTime()); } @Override public Date read(JsonReader in) throws IOException { if (in.peek() == JsonToken.NULL) { in.nextNull(); return null; } // 将毫秒数值转为Date return new Date(in.nextLong()); } }
然后创建Gson实例时注册这个适配器:
import com.google.gson.Gson; import com.google.gson.GsonBuilder; Gson gson = new GsonBuilder() .registerTypeAdapter(Date.class, new DateTimestampAdapter()) .create(); MyDTOClass evData = gson.fromJson(evDataJsonStr, MyDTOClass.class);
方法2:适配Java 8+时间API(LocalDateTime)
如果用LocalDateTime,可以类似自定义适配器:
import com.google.gson.TypeAdapter; import com.google.gson.stream.JsonReader; import com.google.gson.stream.JsonToken; import com.google.gson.stream.JsonWriter; import java.io.IOException; import java.time.Instant; import java.time.LocalDateTime; import java.time.ZoneId; public class LocalDateTimeTimestampAdapter extends TypeAdapter<LocalDateTime> { @Override public void write(JsonWriter out, LocalDateTime value) throws IOException { if (value == null) { out.nullValue(); return; } out.value(value.atZone(ZoneId.systemDefault()).toInstant().toEpochMilli()); } @Override public LocalDateTime read(JsonReader in) throws IOException { if (in.peek() == JsonToken.NULL) { in.nextNull(); return null; } // 毫秒时间戳转LocalDateTime return Instant.ofEpochMilli(in.nextLong()) .atZone(ZoneId.systemDefault()) .toLocalDateTime(); } }
注册后使用即可:
Gson gson = new GsonBuilder() .registerTypeAdapter(LocalDateTime.class, new LocalDateTimeTimestampAdapter()) .create();
额外检查点
- 确认DTO中
startDate的字段类型正确:不要定义为String,要和你期望的日期类型一致(Date/LocalDateTime等)。 - 如果是Spring Boot项目,可以全局配置Jackson,避免每个字段都加注解:在
application.properties里添加:spring.jackson.deserialization.read-date-timestamps-as-nanoseconds=false spring.jackson.serialization.write-dates-as-timestamps=true
内容的提问来源于stack exchange,提问作者Yousaf
相关产品推荐
相关产品推荐

