如何将嵌套结构JSON日期反序列化为Java LocalDateTime对象
解决Jackson反序列化嵌套结构LocalDateTime的问题
项目背景
Spring Boot + Gradle环境下,定义了如下Sale实体类:
public class Sale{ private String storeNumber; @JsonFormat(pattern = "yyyy-MM-dd'T'HH:mm:ss") private LocalDateTime saleDateTime; // getter、setter方法 }
需要将以下嵌套结构的JSON反序列化为Sale对象:
{ "storeNumber": "023", "saleDateTime": { "date": { "year" : 2023, "month" : 4, "day" : 4 }, "time": { "hour" : 16, "minute" : 28, "second" : 45, "nano" : 0 } } }
使用ObjectMapper.readValue(json, Sale.class)时抛出异常:
MismatchedInputException: Cannot deserialize value of type 'java.time.LocalDateTime' from Object value (token 'JsonToken.START_OBJECT')
此前尝试添加jackson-datatype-jsr310依赖、注册JavaTimeModule、编写自定义反序列化器但未生效,以下是可行的解决方案:
解决方案步骤
1. 确认Gradle依赖
确保引入正确的Jackson JSR310模块依赖(版本与Spring Boot适配即可):
implementation 'com.fasterxml.jackson.datatype:jackson-datatype-jsr310'
2. 编写自定义LocalDateTime反序列化器
针对嵌套的date和time节点,实现自定义反序列化逻辑:
import com.fasterxml.jackson.core.JsonParser; import com.fasterxml.jackson.databind.DeserializationContext; import com.fasterxml.jackson.databind.JsonDeserializer; import com.fasterxml.jackson.databind.JsonNode; import java.io.IOException; import java.time.LocalDateTime; import java.time.Month; public class NestedLocalDateTimeDeserializer extends JsonDeserializer<LocalDateTime> { @Override public LocalDateTime deserialize(JsonParser parser, DeserializationContext context) throws IOException { JsonNode rootNode = parser.getCodec().readTree(parser); JsonNode dateNode = rootNode.get("date"); JsonNode timeNode = rootNode.get("time"); // 解析日期部分 int year = dateNode.get("year").asInt(); Month month = Month.of(dateNode.get("month").asInt()); int day = dateNode.get("day").asInt(); // 解析时间部分 int hour = timeNode.get("hour").asInt(); int minute = timeNode.get("minute").asInt(); int second = timeNode.get("second").asInt(); int nano = timeNode.get("nano").asInt(); return LocalDateTime.of(year, month, day, hour, minute, second, nano); } }
3. 注册反序列化器
有两种注册方式,选其一即可:
方式一:字段级注解注册
直接在Sale类的saleDateTime字段上添加@JsonDeserialize注解:
import com.fasterxml.jackson.databind.annotation.JsonDeserialize; import java.time.LocalDateTime; public class Sale { private String storeNumber; @JsonDeserialize(using = NestedLocalDateTimeDeserializer.class) private LocalDateTime saleDateTime; // getter、setter方法 }
方式二:全局ObjectMapper注册
如果需要全局生效,在配置类中自定义ObjectMapper并注册反序列化器:
import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration public class JacksonConfig { @Bean public ObjectMapper objectMapper() { ObjectMapper mapper = new ObjectMapper(); JavaTimeModule module = new JavaTimeModule(); // 添加自定义反序列化器 module.addDeserializer(LocalDateTime.class, new NestedLocalDateTimeDeserializer()); mapper.registerModule(module); return mapper; } }
4. 测试验证
编写测试代码验证反序列化效果:
import com.fasterxml.jackson.databind.ObjectMapper; import java.io.IOException; public class DeserializationTest { public static void main(String[] args) throws IOException { String json = "{\n" + " \"storeNumber\": \"023\",\n" + " \"saleDateTime\": {\n" + " \"date\": {\n" + " \"year\" : 2023,\n" + " \"month\" : 4,\n" + " \"day\" : 4\n" + " },\n" + " \"time\": {\n" + " \"hour\" : 16,\n" + " \"minute\" : 28,\n" + " \"second\" : 45,\n" + " \"nano\" : 0\n" + " }\n" + " }\n" + "}"; ObjectMapper mapper = new ObjectMapper(); // 如果是全局注册,直接注入ObjectMapper即可 mapper.registerModule(new JavaTimeModule().addDeserializer(LocalDateTime.class, new NestedLocalDateTimeDeserializer())); Sale sale = mapper.readValue(json, Sale.class); System.out.println(sale.getSaleDateTime()); // 输出:2023-04-04T16:28:45 } }
问题原因说明
默认的JavaTimeModule仅支持ISO标准格式的日期字符串或单一字段的日期结构,无法解析这种拆分为date和time的嵌套对象,因此必须通过自定义反序列化器手动解析各节点字段并组装成LocalDateTime对象。
内容的提问来源于stack exchange,提问作者Bharadwaj Joshi
相关产品推荐
相关产品推荐

