You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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这类新日期类型,需要额外两步:

  1. 先添加Jackson的Java 8时间模块依赖(Maven示例):
<dependency>
    <groupId>com.fasterxml.jackson.datatype</groupId>
    <artifactId>jackson-datatype-jsr310</artifactId>
    <version>你的Jackson版本</version>
</dependency>
  1. 配置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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.30 08:22:50