转至元数据结尾
转至元数据起始

issue1:使用Jackson反序列化 诸如 yyyy/MM/dd 和 yyyy-MM-dd 或者 MM/dd/yyyy 格式的日期数据。


1)通过使用 @JsonFormat 注解s,可以轻松地反序列化特定格式的日期字符串


2)不添加注解, 通过改变objectMapper的方式,来支持特定格式日期的反序列化

可以通过配置 ObjectMapper 的方式来处理 yyyy/MM/dd 格式的日期,而无需在类中添加注解。可以使用 SimpleDateFormat 来设置全局日期格式。

示例代码

import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;

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

public class DateExample {

    static class MyData {
        private Date date;

        public Date getDate() {
            return date;
        }

        public void setDate(Date date) {
            this.date = date;
        }
    }

    public static void main(String[] args) {
        String json = "{\"date\": \"2024/10/15\"}";

        ObjectMapper objectMapper = new ObjectMapper();
        
        // 设置日期格式
        objectMapper.setDateFormat(new SimpleDateFormat("yyyy/MM/dd"));

        // 可选:启用 Java 8 日期时间模块(如果使用 LocalDate 等)
        objectMapper.registerModule(new JavaTimeModule());
        objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);

        try {
            MyData myData = objectMapper.readValue(json, MyData.class);
            System.out.println("Deserialized Date: " + myData.getDate());
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}


解释

  1. 设置日期格式
    • 使用 objectMapper.setDateFormat(new SimpleDateFormat("yyyy/MM/dd")); 设置全局日期格式。
  2. 反序列化
    • 将 JSON 字符串反序列化为 MyData 对象时,ObjectMapper 会自动使用指定的日期格式。

输出结果

运行上述代码,反序列化后的 Date 对象:

Deserialized Date: Tue Oct 15 00:00:00 UTC 2024

结论

通过修改 ObjectMapper 的配置,可以灵活地处理日期格式,而无需在每个数据类中添加注解。这种方法在需要统一处理多个类的日期格式时尤其有用。


3)通过改变objectMapper的方式,来支持多种格式日期的反序列化

要实现一个 ObjectMapper,既支持 yyyy/MM/dd 格式又支持 yyyy-MM-dd 格式的日期反序列化,可以使用自定义的 DateDeserializer。 下面是示例代码,当然,据此我们依然可以按需扩展其他有效的时间格式,如MM/dd/yyyy

import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.fasterxml.jackson.databind.module.SimpleModule;

import java.io.IOException;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;

public class DateExample {

    static class MyData {
        @JsonDeserialize(using = CustomDateDeserializer.class)
        private Date date;

        public Date getDate() {
            return date;
        }

        public void setDate(Date date) {
            this.date = date;
        }
    }

    public static void main(String[] args) {
        String json1 = "{\"date\": \"2024/10/15\"}";
        String json2 = "{\"date\": \"2024-10-15\"}";

        ObjectMapper objectMapper = new ObjectMapper();
        SimpleModule module = new SimpleModule();
        module.addDeserializer(Date.class, new CustomDateDeserializer());
        objectMapper.registerModule(module);

        try {
            MyData myData1 = objectMapper.readValue(json1, MyData.class);
            System.out.println("Deserialized Date (yyyy/MM/dd): " + myData1.getDate());

            MyData myData2 = objectMapper.readValue(json2, MyData.class);
            System.out.println("Deserialized Date (yyyy-MM-dd): " + myData2.getDate());
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    static class CustomDateDeserializer extends com.fasterxml.jackson.databind.JsonDeserializer<Date> {
        private final SimpleDateFormat format1 = new SimpleDateFormat("yyyy/MM/dd");
        private final SimpleDateFormat format2 = new SimpleDateFormat("yyyy-MM-dd");

        @Override
        public Date deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
            String dateStr = p.getText();
            try {
                // 尝试第一种格式
                return format1.parse(dateStr);
            } catch (ParseException e) {
                // 如果失败,尝试第二种格式
                try {
                    return format2.parse(dateStr);
                } catch (ParseException e1) {
                    // 如果两种格式都失败,抛出异常
                    throw new IOException("Unparseable date: " + dateStr);
                }
            }
        }
    }
}

解释

  1. 自定义反序列化器

    • CustomDateDeserializer 类实现了 JsonDeserializer<Date>,并定义了两个日期格式:yyyy/MM/ddyyyy-MM-dd
    • deserialize 方法中,首先尝试使用第一种格式解析,如果失败,则尝试第二种格式。
  2. 注册自定义反序列化器

    • 创建 SimpleModule 并添加自定义反序列化器,然后将其注册到 ObjectMapper
  3. 反序列化示例

    • 提供两个 JSON 字符串,分别使用不同的日期格式进行反序列化。

输出结果

Deserialized Date (yyyy/MM/dd): Tue Oct 15 00:00:00 UTC 2024
Deserialized Date (yyyy-MM-dd): Tue Oct 15 00:00:00 UTC 2024

结论

通过自定义反序列化器,我们可以灵活地处理多种日期格式,从而满足不同的输入需求。

issue2:SpringMVC如何支持多种有效日期的反序列化

SpringMVC使用Jackson序列化。


在 Spring Boot MVC 中支持多种日期格式的反序列化,可以通过自定义消息转换器或使用自定义的 `@InitBinder` 方法来实现。以下是两种常见的方法。

### 方法 1:自定义消息转换器

1. **创建自定义日期反序列化器**:

import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonDeserializer;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.context.annotation.Bean;
import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;

import java.io.IOException;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;

@EnableWebMvc
public class WebConfig {

    @Bean
    public ObjectMapper objectMapper() {
        Jackson2ObjectMapperBuilder builder = new Jackson2ObjectMapperBuilder();
        builder.deserializerByType(Date.class, new CustomDateDeserializer());
        return builder.build();
    }

    static class CustomDateDeserializer extends JsonDeserializer<Date> {
        private final SimpleDateFormat format1 = new SimpleDateFormat("yyyy/MM/dd");
        private final SimpleDateFormat format2 = new SimpleDateFormat("yyyy-MM-dd");

        @Override
        public Date deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
            String dateStr = p.getText();
            try {
                return format1.parse(dateStr);
            } catch (ParseException e) {
                try {
                    return format2.parse(dateStr);
                } catch (ParseException e1) {
                    throw new IOException("Unparseable date: " + dateStr);
                }
            }
        }
    }
}



### 方法 2:使用 `@InitBinder`

1. **创建控制器并使用 `@InitBinder`**:

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.WebDataBinder;
import org.springframework.web.bind.annotation.InitBinder;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.ResponseBody;

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

@Controller
public class MyController {

    @InitBinder
    public void initBinder(WebDataBinder binder) {
        binder.registerCustomEditor(Date.class, new CustomDateEditor());
    }

    @PostMapping("/submit")
    @ResponseBody
    public String submit(@RequestBody MyData data) {
        return "Received date: " + data.getDate();
    }

    static class CustomDateEditor extends PropertyEditorSupport {
        private final SimpleDateFormat format1 = new SimpleDateFormat("yyyy/MM/dd");
        private final SimpleDateFormat format2 = new SimpleDateFormat("yyyy-MM-dd");

        @Override
        public void setAsText(String text) throws IllegalArgumentException {
            try {
                if (text.contains("/")) {
                    setValue(format1.parse(text));
                } else {
                    setValue(format2.parse(text));
                }
            } catch (ParseException e) {
                throw new IllegalArgumentException("Invalid date format");
            }
        }
    }

    static class MyData {
        private Date date;

        public Date getDate() {
            return date;
        }

        public void setDate(Date date) {
            this.date = date;
        }
    }
}


### 总结

- **自定义消息转换器**:通过定制 `ObjectMapper`,在全局范围内支持多种日期格式。
- **`@InitBinder`**:在控制器中设置自定义日期编辑器,适用于特定的请求处理。

两种方法都可以有效地支持多种日期格式的反序列化,选择适合我们应用场景的方法即可。



编写评论...