springboot接收Date类型数据异常与解决办法

本文介绍在SpringBoot中处理前端日期传值的两种方法:局部配置使用@DateTimeFormat注解,适用于特定格式;全局配置自定义时间转换器,处理多种日期格式,包括时间戳。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >


默认情况下

在默认情况下,不加任何有关接收Date类型数据的配置时,前端传递Date类型数据至后端接口,控制台出现以下异常:

Failed to convert from type [java.lang.String] to type [java.util.Date] for value
 '2333333333'; nested exception is java.lang.IllegalArgumentException]]
复制代码

也就是说,在SpringBoot中,必须添加某种配置才能让前端正确传递时间类型数据到后端。下面针对不同的情况来介绍一下解决办法。


局部配置——使用@DateTimeFormat

第一种情况,仅需要对某个Bean类的Date类型字段进行转换,那么只需要在Bean类属性上增加@DateTimeFormat()注解,括号内 pattern为前端传递的日期格式。比如:

@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
Date createTime;
复制代码

特点--缺陷:

按照如上配置,只能处理形如:2018-11-2 2:22:2这样固定格式的数据,无法处理时间戳和2018-11-2格式的数据。如果想要处理2018-11-2这样的时间,就必须把pattern改成yyyy-MM-dd

可以对不同的属性赋予不同的pattern,但是对每个Date类型都要加上注解显得比较繁琐,也无法处理单个属性可能对应不同格式的值的情况。

更通用、有效的方式是定义一个时间转换类,并将其应用到所有的接口上。


全局配置——自定义时间转换器

分为两个步骤:

  • 编写一个时间转换类,把可能的格式转换为Date类型
  • 通过某种方式,将时间转换应用到Spring的所有接口上

时间转换类

import org.springframework.core.convert.converter.Converter;
import org.springframework.util.StringUtils;

import java.text.SimpleDateFormat;
import java.util.Date;

/**
 * 日期转换类
 * 将标准日期、标准日期时间、时间戳转换成Date类型
 */
public class DateConverter implements Converter<String, Date> {
    private static final String dateFormat = "yyyy-MM-dd HH:mm:ss";
    private static final String shortDateFormat = "yyyy-MM-dd";
    private static final String timeStampFormat = "^\\d+$";

    @Override
    public Date convert(String value) {

        if(StringUtils.isEmpty(value)) {
            return null;
        }

        value = value.trim();

        try {
            if (value.contains("-")) {
                SimpleDateFormat formatter;
                if (value.contains(":")) {
                    formatter = new SimpleDateFormat(dateFormat);
                } else {
                    formatter = new SimpleDateFormat(shortDateFormat);
                }
                return formatter.parse(value);
            } else if (value.matches(timeStampFormat)) {
                Long lDate = new Long(value);
                return new Date(lDate);
            }
        } catch (Exception e) {
            throw new RuntimeException(String.format("parser %s to Date fail", value));
        }
        throw new RuntimeException(String.format("parser %s to Date fail", value));
    }
}
复制代码

将时间转换类应用到接口上

介绍两种方式:使用@Component + @PostConstruct@ControllerAdvice + @InitBinder


第一种方式:

@Component + @PostConstruct
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.convert.support.GenericConversionService;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.support.ConfigurableWebBindingInitializer;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter;

import javax.annotation.PostConstruct;

/**
 * @author zfh
 * @version 1.0
 * @date 2018/12/30 10:16
 */
@Component
public class WebConfigBeans {

  @Autowired
  private RequestMappingHandlerAdapter handlerAdapter;

  @PostConstruct
  public void initEditableAvlidation() {

    ConfigurableWebBindingInitializer initializer = (ConfigurableWebBindingInitializer)handlerAdapter.getWebBindingInitializer();
    if(initializer.getConversionService()!=null) {
      GenericConversionService genericConversionService = (GenericConversionService)initializer.getConversionService();

      genericConversionService.addConverter(new DateConverterConfig());

    }
  }
}
复制代码

第二种方式:

@ControllerAdvice + @InitBinder

有关这两个注解的含义请参考我的博客:Spring进阶之@ControllerAdvice与统一异常处理

import com.aegis.config.converter.DateConverter;
import com.aegis.model.bean.common.JsonResult;
import org.springframework.core.convert.support.GenericConversionService;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.WebDataBinder;
import org.springframework.web.bind.annotation.*;

/**
 * @author zfh
 * @version 1.0
 * @since 2019/1/4 15:23
 */
@ControllerAdvice
public class ControllerHandler {

    @InitBinder
    public void initBinder(WebDataBinder binder) {
        GenericConversionService genericConversionService = (GenericConversionService) binder.getConversionService();
        if (genericConversionService != null) {
            genericConversionService.addConverter(new DateConverter());
        }
    }
}
复制代码

OK,关于SpringBoot处理前端日期传值的问题的相关解决办法就介绍到这里了。

转载于:https://juejin.im/post/5cbe6c395188250a9062dc85

<think>我们使用Spring Boot来接收客户端来的JSON数据并反序列化为Java对象。Spring Boot默认使用Jackson库来处理JSON的序列化和反序列化。以下是实现的步骤: 1. **添加依赖**:Spring Boot Web starter已经包含了Jackson,所以通常不需要额外添加依赖。 2. **创建Controller**:使用`@RestController`注解的类来处理HTTP请求。 3. **定义数据模型**:创建Java类来表示要反序列化的JSON数据结构。 4. **使用`@RequestBody`注解**:在Controller的方法参数上使用`@RequestBody`注解,Spring会自动将入的JSON数据反序列化为对应的Java对象。 ### 步骤1: 添加依赖(如果尚未添加) 在`pom.xml`中确保有Spring Boot Web依赖: ```xml <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> </dependencies> ``` ### 步骤2: 定义数据模型 创建一个Java类,其属性JSON字段对应。例如,假设客户端发送的JSON数据如下: ```json { "name": "张三", "age": 25 } ``` 则对应的Java类为: ```java public class UserData { private String name; private int age; // 必须有无参构造函数(Jackson反序列化需要) public UserData() { } // Getter和Setter方法 public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } } ``` ### 步骤3: 创建Controller处理请求 在Controller中,使用`@PostMapping`(或其他HTTP方法映射注解)来指定接收请求的路径,并在方法参数中使用`@RequestBody`来接收JSON数据并自动反序列化。 ```java import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RestController; @RestController public class UserController { @PostMapping("/user") public String createUser(@RequestBody UserData userData) { // 这里userData已经是反序列化后的Java对象 System.out.println("用户名: " + userData.getName()); System.out.println("年龄: " + userData.getAge()); // 处理业务逻辑... return "用户创建成功"; } } ``` ### 关键点说明 - **`@RequestBody`**:这个注解告诉Spring将HTTP请求体(JSON数据)反序列化为一个`UserData`对象[^1]。 - **字段匹配**:JSON中的字段名必须Java类中的属性名一致(或者通过注解指定映射关系,如`@JsonProperty`)[^3]。 - **无参构造函数**:Jackson在反序列化时需要无参构造函数(如果类中没有定义任何构造函数,Java会默认提供无参构造;如果定义了带参构造,必须显式添加无参构造)[^1]。 - **Getter和Setter**:虽然Jackson可以直接访问字段,但提供Getter和Setter是推荐的做法(特别是需要序列化时)[^1]。 ### 自定义反序列化 如果需要对某些字段进行特殊处理(如日期格式、自定义逻辑),可以使用Jackson的注解或自定义反序列化器[^5]。 #### 示例:使用`@JsonFormat`处理日期 假设JSON中包含日期字段: ```json { "name": "张三", "age": 25, "birthday": "1990-01-01" } ``` 在Java类中: ```java public class UserData { private String name; private int age; @JsonFormat(pattern = "yyyy-MM-dd") private Date birthday; // 省略其他代码 } ``` #### 示例:自定义反序列化器 当默认行为无法满足需求时,可以自定义反序列化器。例如,将字符串"男"或"女"反序列化为枚举`Gender`: ```java public enum Gender { MALE, FEMALE } ``` 自定义反序列化器: ```java import com.fasterxml.jackson.core.JsonParser; import com.fasterxml.jackson.databind.DeserializationContext; import com.fasterxml.jackson.databind.JsonDeserializer; import java.io.IOException; public class GenderDeserializer extends JsonDeserializer<Gender> { @Override public Gender deserialize(JsonParser p, DeserializationContext ctxt) throws IOException { String text = p.getText(); if ("男".equals(text)) { return Gender.MALE; } else if ("女".equals(text)) { return Gender.FEMALE; } throw new IllegalArgumentException("无效的性别值"); } } ``` 在字段上使用自定义反序列化器: ```java public class UserData { private String name; private int age; @JsonDeserialize(using = GenderDeserializer.class) private Gender gender; // 省略其他代码 } ``` ### 测试 使用Postman或curl发送POST请求: - URL: `http://localhost:8080/user` - Body (JSON): ```json { "name": "张三", "age": 25 } ``` ### 注意事项 - **JSON字段Java属性匹配**:默认情况下,Jackson通过字段名匹配。如果JSON字段名Java属性名不一致,可以使用`@JsonProperty`注解: ```java @JsonProperty("full_name") private String fullName; ``` - **忽略未知字段**:默认情况下,如果JSON中有Java类不存在的字段,Jackson会抛出异常。可以通过在类上添加`@JsonIgnoreProperties(ignoreUnknown = true)`来忽略未知字段[^3]: ```java @JsonIgnoreProperties(ignoreUnknown = true) public class UserData { // ... } ``` - **集合类型**:也可以反序列化为集合类型,如`List<UserData>`: ```java @PostMapping("/users") public String createUsers(@RequestBody List<UserData> users) { // ... } ``` 通过以上步骤,Spring Boot可以轻松接收并反序列化客户端来的JSON数据。 ### 相关问题 1. 如何在Spring Boot中自定义JSON序列化和反序列化规则? 2. Spring Boot如何处理JSON数据中的日期格式? 3. 在Spring Boot中如何忽略JSON中的未知字段? 4. 如何验证反序列化后的对象数据(如使用Hibernate Validator)?</think>### 在Spring Boot中接收JSON数据并反序列化的实现 Spring Boot提供了强大的JSON处理能力,默认使用Jackson库进行序列化和反序列化操作。以下是完整实现步骤: #### 1. 添加依赖(确保pom.xml包含) ```xml <dependencies> <!-- Spring Web MVC --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <!-- Jackson核心库(通常已包含在web中) --> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> </dependency> </dependencies> ``` #### 2. 创建数据模型类 ```java public class UserData { private String name; private int age; // 必须有无参构造函数 public UserData() {} // Getter和Setter方法(Jackson通过反射访问) public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } } ``` #### 3. 创建Controller接收请求 ```java import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RestController; @RestController public class UserController { // 使用@RequestBody自动反序列化JSON @PostMapping("/users") public String createUser(@RequestBody UserData userData) { System.out.println("接收用户: " + userData.getName() + ", 年龄: " + userData.getAge()); return "用户创建成功"; } } ``` #### 4. 自定义序列化/反序列化(可选) 当需要特殊处理字段时,可使用Jackson注解: ```java import com.fasterxml.jackson.annotation.JsonFormat; import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonProperty; public class CustomUser { @JsonProperty("user_name") // JSON字段重命名 private String name; @JsonIgnore // 忽略此字段 private String password; @JsonFormat(pattern = "yyyy-MM-dd") // 日期格式化 private Date birthDate; // Getter/Setter省略 } ``` #### 5. 使用ObjectMapper手动处理 需要精细控制时直接使用ObjectMapper: ```java import com.fasterxml.jackson.databind.ObjectMapper; @RestController public class ManualController { @PostMapping("/manual") public String manualDeserialize(@RequestBody String json) throws Exception { ObjectMapper objectMapper = new ObjectMapper(); UserData user = objectMapper.readValue(json, UserData.class); // 处理复杂类型 Map<String, Object> map = objectMapper.readValue(json, new TypeReference<Map<String, Object>>() {}); return "处理成功"; } } ``` #### 6. 测试请求示例 使用curl测试: ```bash curl -X POST http://localhost:8080/users \ -H "Content-Type: application/json" \ -d '{"name":"张三", "age":25}' ``` #### 关键点说明 1. **`@RequestBody`注解**:自动将请求体中的JSON反序列化为Java对象[^1] 2. **字段匹配规则**:JSON属性名默认Java字段名匹配(大小写不敏感) 3. **日期处理**:使用`@JsonFormat`定义日期格式[^3] 4. **忽略字段**:`@JsonIgnore`跳过序列化/反序列化[^3] 5. **类型安全**:复杂泛型使用`TypeReference`[^1] 6. **错误处理**:添加全局异常处理器捕获反序列化错误 #### 完整Controller示例 ```java @RestController public class UserController { @PostMapping("/users") public ResponseEntity<String> createUser(@Valid @RequestBody UserData userData) { // 业务处理逻辑 return ResponseEntity.ok("用户创建成功: " + userData.getName()); } // 全局异常处理 @ExceptionHandler(HttpMessageNotReadableException.class) public ResponseEntity<String> handleJsonError() { return ResponseEntity.badRequest().body("JSON格式错误"); } } ``` ### 常见问题解决方案 1. **字段不匹配问题**: - 使用`@JsonProperty("json_field")`显式映射 - 在application.properties中配置: ```properties spring.jackson.property-naming-strategy=SNAKE_CASE ``` 2. **日期格式处理**: ```java public class User { @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") private Date registerTime; } ``` 3. **忽略未知字段**: ```java @JsonIgnoreProperties(ignoreUnknown = true) public class UserData { // ... } ``` 4. **自定义序列化器**: ```java public class MoneySerializer extends JsonSerializer<BigDecimal> { @Override public void serialize(BigDecimal value, JsonGenerator gen, SerializerProvider provider) { gen.writeString(value.setScale(2) + "元"); } } public class Product { @JsonSerialize(using = MoneySerializer.class) private BigDecimal price; } ``` ### 性能优化建议 1. 重用ObjectMapper实例(Spring默认单例) 2. 对于大文件流式处理: ```java JsonFactory factory = new JsonFactory(); JsonParser parser = factory.createParser(inputStream); while (parser.nextToken() != null) { // 流式处理 } ``` 通过以上实现,Spring Boot可以高效接收并反序列化客户端来的JSON数据[^1][^3][^5]。 --- ### 相关问题 1. 如何在Spring Boot中自定义JSON字段的命名策略? 2. 如何处理JSON中的日期/时间格式转换问题? 3. 如何为特定字段实现自定义的序列化/反序列化逻辑? 4. 在微服务架构中如何确保多个服务间的JSON数据兼容性?
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值