Spring + Jackson + joda time:如何指定序列化/反序列化格式?

Posted

技术标签:

【中文标题】Spring + Jackson + joda time:如何指定序列化/反序列化格式?【英文标题】:Spring + Jackson + joda time: how to specify the serialization/deserialization format? 【发布时间】:2014-01-06 01:46:48 【问题描述】:

我有以下课程:

public static class ARestRequestParam

    String name;
    LocalDate date;  // joda type

我希望它从杰克逊处理的以下 JSON 中反序列化。

名称:“abc”,日期:“20131217”

实际上,我想反序列化任何具有“yyyyMMdd”格式的类中的任何LocalDate 字段,不复制格式字符串,不添加任何setter 方法,不进行任何XML 配置。 (即注解和Java代码更可取) 怎么办?

另外,我也想知道序列化部分。即 LocalDate -> "yyyyMMdd"。

我看过以下内容:

jackson-datatype-joda (https://github.com/FasterXML/jackson-datatype-joda) 自定义序列化程序(公共类 JodaDateTimeJsonSerializer 扩展 JsonSerializer ... - Spring @ResponseBody Jackson JsonSerializer with JodaTime) @JsonCreator @DateTimeFormat

但我不知道哪个适用,哪个是最新的。

顺便说一句,我使用的是 Spring Boot。

更新

好的,我已经设法为反序列化部分编写了工作代码。 如下:

@Configuration
@EnableWebMvc
public class WebMvcConfiguration extends WebMvcConfigurerAdapter

    @Override
    public void configureMessageConverters(
        List<HttpMessageConverter<?>> converters)
    
        converters.add(jacksonConverter());
    

    @Bean
    public MappingJackson2HttpMessageConverter jacksonConverter()
    
        MappingJackson2HttpMessageConverter converter =
            new MappingJackson2HttpMessageConverter();

        ObjectMapper mapper = new ObjectMapper();
        mapper.registerModule(new ApiJodaModule());
        converter.setObjectMapper(mapper);

        return converter;
    

    @SuppressWarnings("serial")
    private class ApiJodaModule extends SimpleModule
    
        public ApiJodaModule()
        
            addDeserializer(LocalDate.class, new ApiLocalDateDeserializer());
        
    

    @SuppressWarnings("serial")
    private static class ApiLocalDateDeserializer
        extends StdScalarDeserializer<LocalDate>
    
        private static DateTimeFormatter formatter =
            DateTimeFormat.forPattern("yyyyMMdd");

        public ApiLocalDateDeserializer()  super(LocalDate.class); 

        @Override
        public LocalDate deserialize(JsonParser jp, DeserializationContext ctxt)
            throws IOException, JsonProcessingException
        
            if (jp.getCurrentToken() == JsonToken.VALUE_STRING)
            
                String s = jp.getText().trim();
                if (s.length() == 0)
                    return null;
                return LocalDate.parse(s, formatter);
            
            throw ctxt.wrongTokenException(jp, JsonToken.NOT_AVAILABLE,
                "expected JSON Array, String or Number");
        
    

我必须自己实现反序列化器,因为无法更改 jackson-datatype-joda 中反序列化器的日期时间格式。因此,由于我自己实现了反序列化器,因此不需要 jackson-datatype-joda。 (虽然我复制了它的部分代码)

这段代码可以吗? 这是最新的解决方案吗? 还有其他更简单的方法吗? 任何建议将不胜感激。

更新

根据 Dave Syer 的建议,我将上面的源代码修改如下:

删除了 2 个方法:configureMessageConverters()、jacksonConverter() 在 WebMvcConfiguration 类中添加了以下方法:

@Bean
public Module apiJodaModule()

    return new ApiJodaModule();

但现在它不起作用。似乎 apiJodaModule() 被忽略了。 我怎样才能让它工作? (看来我不应该有一个具有@EnableWebMvc 的类来使用该功能。)

我使用的版本是org.springframework.boot:spring-boot-starter-web:0.5.0.M6。

更新

最终的工作版本如下:(以及我之前在具有@EnableWebMvc 的类中完成的其他配置) 正如 Dave Syer 所提到的,这仅适用于 BUILD-SNAPSHOT 版本,至少目前是这样。

@Configuration
public class WebMvcConfiguration

    @Bean
    public WebMvcConfigurerAdapter apiWebMvcConfiguration()
    
        return new ApiWebMvcConfiguration();
    

    @Bean
    public UserInterceptor userInterceptor()
    
        return new UserInterceptor();
    

    public class ApiWebMvcConfiguration extends WebMvcConfigurerAdapter
    
        @Override
        public void addInterceptors(InterceptorRegistry registry)
        
            registry.addInterceptor(userInterceptor())
                .addPathPatterns("/api/user/**");
        

        @Override
        public void addResourceHandlers(ResourceHandlerRegistry registry)
        
            registry.addResourceHandler("/**")
                .addResourceLocations("/")
                .setCachePeriod(0);
        
    

    @Bean
    public Module apiJodaModule()
    
        return new ApiJodaModule();
    

    @SuppressWarnings("serial")
    private static class ApiJodaModule extends SimpleModule
    
        public ApiJodaModule()
        
            addDeserializer(LocalDate.class, new ApiLocalDateDeserializer());
        

        private static final class ApiLocalDateDeserializer
            extends StdScalarDeserializer<LocalDate>
        
            public ApiLocalDateDeserializer()  super(LocalDate.class); 

            @Override
            public LocalDate deserialize(JsonParser jp,
                DeserializationContext ctxt)
                throws IOException, JsonProcessingException
            
                if (jp.getCurrentToken() == JsonToken.VALUE_STRING)
                
                    String s = jp.getText().trim();
                    if (s.length() == 0)
                        return null;
                    return LocalDate.parse(s, localDateFormatter);
                
                throw ctxt.mappingException(LocalDate.class);
            
        

        private static DateTimeFormatter localDateFormatter =
            DateTimeFormat.forPattern("yyyyMMdd");
    

【问题讨论】:

【参考方案1】:

您的代码没问题,但如果您在 Spring Boot 应用程序中使用 @EnableWebMvc,您会关闭框架中的默认设置,所以也许您应该避免这种情况。此外,您的 MVC 处理程序适配器中现在只有 一个 HttpMessageConverter。如果您使用 Spring Boot 的快照,您应该能够简单地定义类型为 Module@Bean,其他所有操作都是自动的,因此我建议您这样做。

【讨论】:

谢谢。实际上,我必须将 addResourceHandlers() 添加到 WebMvcConfiguration 类以提供静态文件(因为默认设置已消失)。但是由于我覆盖了 addInterceptors() 以添加 URL 拦截器,因此我必须创建自己的具有 EnableWebMvc 的类。 您不需要@EnableWebMvc 来添加拦截器,只需添加WebMvcConfigurerAdapter 类型的@Bean(标准Spring MVC 功能)。阅读答案:“使用快照”。那应该可以解决您的问题。 谢谢。通过阅读spring源代码,我刚刚意识到可以有多个WebMvcConfigurerAdapter。顺便说一句,我不知道“使用快照”的确切含义。如何在 build.gradle 文件中指定它? (我对Java开发环境的了解太少了)由于HttpMessageConvertersAutoConfiguration.java是2天前修改的,可能M6没有这个功能。那么,我应该从github下载spring boot吗? 不,它不包含在 M6 中,这就是您需要快照的原因。如果您有正确的存储库配置(遵循website),那么您只需将版本标签从“M6”更改为“BUILD-SNAPSHOT”。 非常感谢。我会试试的。很抱歉问了这么一个基本的问题。顺便说一句,直到今天我才意识到'M6'中的'M'意味着'里程碑'......

以上是关于Spring + Jackson + joda time:如何指定序列化/反序列化格式?的主要内容,如果未能解决你的问题,请参考以下文章

如何正确地在Spring Data JPA和Jackson中用上Java 8的时间相关API(即JSR 310也即java.time包下的众神器)

使用 Jackson 进行 DateTime 反序列化的默认时区(Joda-Time 模块)

Jackson 使用自定义日期格式错误地反序列化 Joda 日期

如何使用 Jackson JSON 处理器序列化 Joda DateTime?

Jackson 在 Spring Boot Rest 应用程序中将日期更改为一天。

springboot 时间类型配置