【问题标题】:Jackson automatic formatting of Joda DateTime to ISO 8601 format杰克逊自动将 Joda DateTime 格式化为 ISO 8601 格式
【发布时间】:2012-12-11 03:43:41
【问题描述】:

根据http://wiki.fasterxml.com/JacksonFAQDateHandling,“DateTime 可以自动序列化/反序列化,类似于处理 java.util.Date 的方式。”但是,我无法完成这个自动功能。有与此主题相关的 StackOverflow 讨论,但大多数都涉及基于代码的解决方案,但根据上面的引用,我应该能够通过简单的配置来完成此操作。

根据http://wiki.fasterxml.com/JacksonFAQDateHandling,我设置了我的配置,以便将日期写为时间戳是错误的。结果是 java.util.Date 类型被序列化为 ISO 8601 格式,但 org.joda.time.DateTime 类型被序列化为长对象表示。

我的环境是这样的:

杰克逊 2.1
乔达时间 2.1
春季 3.2
Java 1.6

我的 jsonMapper bean 的 Spring 配置是

@Bean
public ObjectMapper jsonMapper() {
    ObjectMapper objectMapper = new ObjectMapper();

    //Fully qualified path shows I am using latest enum
    ObjectMapper.configure(com.fasterxml.jackson.databind.SerializationFeature.
        WRITE_DATES_AS_TIMESTAMPS , false);

    return objectMapper;
}

我的测试代码sn-p是这样的

Date d = new Date();
DateTime dt = new DateTime(d); //Joda time 
Map<String, Object> link = new LinkedHashMap<String, Object>();
link.put("date", d);
link.put("createdDateTime", dt);

JSON 输出的结果 sn-p 是这样的:

{"date":"2012-12-24T21:20:47.668+0000"}

{"createdDateTime": {"year":2012,"dayOfMonth":24,"dayOfWeek":1,"era":1,"dayOfYear":359,"centuryOfEra":20,"yearOfEra":2012,"yearOfCentury":12,"weekyear":2012,"monthOfYear":12 *... remainder snipped for brevity*}}

我的期望是 DateTime 对象应该与基于配置的 Date 对象匹配。我做错了什么,或者我误解了什么?我是不是对 Jackson 文档中的 automatically 这个词读得太多了,而事实上,虽然不是 ISO 8601,但产生了一个字符串表示,正在产生广告中的自动功能?

【问题讨论】:

  • 这似乎已经解决了,但值得指出的是,自动日期支持是指 JDK 中包含的类型——Jackson 默认不支持外部日期库(如 Joda)的日期类型。通过扩展模块添加支持(对于 Joda,通过 github.com/FasterXML/jackson-datatype-joda
  • @StaxMan,我绝对同意。作为 Jackson 的新手,FAQ 中的“DateTime 可以自动 序列化/反序列化,类似于处理 java.util.Date 的方式”声明让我相信这是核心功能。当我发布到 Jackson 用户邮件列表时,有人将我引导到您提到的 github 项目链接,在使用说明中我发现需要处理 JodaModule(通过 jackson-datatype-joda 项目提供) Joda DateTime 并且该模块必须向 ObjectMapper 注册,如答案所示。
  • 好的,我将更新该 Wiki 页面,说明对 Jackson 2.x 的支持与 1.x 不同。谢谢!
  • 基于代码的解决方案在哪里??

标签: json spring jackson jodatime


【解决方案1】:

我能够从 Jackson 用户邮件列表中得到答案,并希望与您分享,因为这是一个新手问题。通过阅读 Jackson Date 常见问题解答,我没有意识到需要额外的依赖项和注册,但事实就是如此。它记录在 git hub 项目页面https://github.com/FasterXML/jackson-datatype-joda

基本上,我必须向特定于 Joda 数据类型的 Jackson jar 添加另一个依赖项,然后我必须在对象映射器上注册该模块的使用。代码sn-ps如下。

对于我的 Jackson Joda 数据类型 Maven 依赖项设置,我使用了这个:

<dependency>
    <groupId>com.fasterxml.jackson.datatype</groupId>
    <artifactId>jackson-datatype-joda</artifactId>
    <version>${jackson.version}</version>
</dependency>

为了注册 Joda 序列化/反序列化功能,我使用了这个:

ObjectMapper objectMapper = new ObjectMapper();
objectMapper.registerModule(new JodaModule());
objectMapper.configure(com.fasterxml.jackson.databind.SerializationFeature.
    WRITE_DATES_AS_TIMESTAMPS , false);

【讨论】:

  • 你把这段代码放在哪里?因为我的 Spring 4.0.1 似乎使用了不同的 ObjectMapper 实例(在 AllEncompassingFormHttpMessageConverter 中创建,请参阅新的 MappingJackson2HttpMessageConverter)
  • 如果您要使用 Spring 中的 Jackson2ObjectMapperFactoryBean,则无需显式注册 JodaModule。它在内部使用Jackson2ObjectMapperBuilder,如果它在类路径上可用,它会自动注册模块。
  • 很可能您还需要添加objectMapper.setDateFormat(new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssz"));,如stackoverflow.com/questions/3269459/…中所述
【解决方案2】:

使用 Spring Boot。

添加到您的 Maven 配置...

<dependency>
  <groupId>com.fasterxml.jackson.datatype</groupId>
  <artifactId>jackson-datatype-joda</artifactId>
  <version>2.7.5</version>
</dependency>

然后到您的 WebConfiguration...

@Configuration
public class WebConfiguration extends WebMvcConfigurerAdapter
{
    public void configureMessageConverters(List<HttpMessageConverter<?>> converters)
    {
      final MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter();
      final ObjectMapper objectMapper = new ObjectMapper();

      //configure Joda serialization
      objectMapper.registerModule(new JodaModule());
      objectMapper.configure(
          com.fasterxml.jackson.databind.SerializationFeature.
            WRITE_DATES_AS_TIMESTAMPS , false);

      // Other options such as how to deal with nulls or identing...
      objectMapper.setSerializationInclusion (
         JsonInclude.Include.NON_NULL);
      objectMapper.enable(SerializationFeature.INDENT_OUTPUT);

      converter.setObjectMapper(objectMapper);
      converters.add(converter);
      super.configureMessageConverters(converters);
    }
}

【讨论】:

  • 在 mongo db "timeStamp":1504051744068, 上为我工作现在正确为 "timeStamp":"2017-10-03T20:26:04.127Z",
【解决方案3】:

在 Spring Boot 中,配置更加简单。您只需声明 Maven 依赖项

    <dependency>
        <groupId>com.fasterxml.jackson.datatype</groupId>
        <artifactId>jackson-datatype-joda</artifactId>
    </dependency>

然后将配置参数添加到您的 application.yml/properties 文件中:

spring.jackson.serialization.write-dates-as-timestamps: false

【讨论】:

    【解决方案4】:

    我想我会使用以下方法发布一个更新的工作示例: Spring 4.2.0.RELEASE、Jackson 2.6.1、Joda 2.8.2

    <?xml version="1.0" encoding="UTF-8"?>
    <beans:beans xmlns="http://www.springframework.org/schema/mvc"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:beans="http://www.springframework.org/schema/beans"
        xmlns:p="http://www.springframework.org/schema/p" xmlns:context="http://www.springframework.org/schema/context"
        xmlns:util="http://www.springframework.org/schema/util"
        xsi:schemaLocation="http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd
            http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
            http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
            http://www.springframework.org/schema/util  http://www.springframework.org/schema/util/spring-util.xsd
            ">
    
        <!-- DispatcherServlet Context: defines this servlet's request-processing 
            infrastructure -->
    
        <!-- Enables the Spring MVC @Controller programming model -->
        <annotation-driven>
            <message-converters>
                <beans:bean
                    class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">
                    <beans:property name="objectMapper" ref="objectMapper" />
                </beans:bean>
            </message-converters>
        </annotation-driven>
    
        <!-- Handles HTTP GET requests for /resources/** by efficiently serving 
            up static resources in the ${webappRoot}/resources directory -->
        <resources mapping="/resources/**" location="/resources/" />
    
        <!-- Resolves views selected for rendering by @Controllers to .jsp resources 
            in the /WEB-INF/views directory -->
        <beans:bean
            class="org.springframework.web.servlet.view.InternalResourceViewResolver">
            <beans:property name="prefix" value="/WEB-INF/views/" />
            <beans:property name="suffix" value=".jsp" />
        </beans:bean>
    
        <beans:bean id="objectMapper"
            class="org.springframework.http.converter.json.Jackson2ObjectMapperFactoryBean">
            <beans:property name="featuresToDisable">
                <beans:array>
                    <util:constant
                        static-field="com.fasterxml.jackson.databind.SerializationFeature.WRITE_DATES_AS_TIMESTAMPS" />
                </beans:array>
            </beans:property>
            <beans:property name="modulesToInstall"
                value="com.fasterxml.jackson.datatype.joda.JodaModule" />
        </beans:bean>
    
        <beans:bean id="localeResolver"
            class="org.springframework.web.servlet.i18n.SessionLocaleResolver">
            <beans:property name="defaultLocale" value="en" />
        </beans:bean>
    
        <!-- Configure the Message Locale Resources -->
        <beans:bean id="messageSource"
            class="org.springframework.context.support.ResourceBundleMessageSource">
            <beans:property name="basename" value="errors" />
        </beans:bean>
    
        <beans:bean id="versionSource"
            class="org.springframework.context.support.ResourceBundleMessageSource">
            <beans:property name="basename" value="version" />
        </beans:bean>
    
    
        <!-- DataSource -->
        <beans:bean id="dataSource"
            class="org.springframework.jndi.JndiObjectFactoryBean">
            <beans:property name="jndiName" value="java:comp/env/jdbc/TestDB" />
        </beans:bean>
    
    
        <!-- POJO: Configure the DAO Implementation -->
        <beans:bean id="publicationsDAO"
            class="com.test.api.publication.PublicationsDAOJdbcImpl">
            <beans:property name="dataSource" ref="dataSource" />
        </beans:bean>
    
    
        <!-- Things to auto-load -->
        <context:component-scan base-package="com.test.api" />
        <context:component-scan base-package="com.test.rest" />
    
    </beans:beans>
    

    API 代码

    package com.test.api.publication;
    
    import java.util.Map;
    
    import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
    import com.fasterxml.jackson.annotation.JsonProperty;
    import com.fasterxml.jackson.annotation.JsonRootName;
    
    @JsonRootName("event")
    @JsonIgnoreProperties(ignoreUnknown=true)
    public class Publication {
    
        private Map<String, Object> tokens;
        private String href;
        private String policy_path;
    
        @JsonProperty("tokens")
        public Map<String, Object> getTokens() {
            return tokens;
        }
    
        @JsonProperty("tokens")
        public void setTokens(Map<String, Object> tokens) {
            this.tokens = tokens;
        }
    
        @JsonProperty("href")
        public String getHref() {
            return href;
        }
    
        @JsonProperty("href")
        public void setHref(String href) {
            this.href = href;
        }
    
        @JsonProperty("policyPath")
        public String getPolicyPath() {
            return policy_path;
        }
    
        @JsonProperty("policyPath")
        public void setPolicyPath(String policy_path) {
            this.policy_path = policy_path;
        }
    
    
    }
    
    
    package com.test.api.publication;
    
    import javax.sql.DataSource;
    
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    
    public class PublicationsDAOJdbcImpl implements PublicationsDAO{
    
        static final Logger logger = LoggerFactory.getLogger(PublicationsDAOJdbcImpl.class.getName());
        private DataSource _dataSource;
    
        @Override
        public void setDataSource(DataSource ds) {
            // TODO Auto-generated method stub
    
        }
    
        @Override
        public void close() {
            // TODO Auto-generated method stub
    
        }
    
        @Override
        public Publication getPublication(String policyPath) {
            Publication ret = new Publication();
            //TODO: do something
            return ret;
        }
    
    }
    
    
    
    package com.test.rest.publication;
    
    import java.util.HashMap;
    
    import org.joda.time.DateTime;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.beans.factory.annotation.Qualifier;
    import org.springframework.web.bind.annotation.PathVariable;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RequestMethod;
    import org.springframework.web.bind.annotation.RestController;
    
    import com.test.api.publication.Publication;
    import com.test.api.publication.PublicationsDAO;
    import com.test.rest.error.UnknownResourceException;
    
    @RestController
    @RequestMapping("/pub")
    public class PublicationController {
    
            private static final Logger logger = LoggerFactory.getLogger(PublicationController.class);
    
            @Autowired
            @Qualifier("publicationsDAO")
            private PublicationsDAO publicationsDAO;
    
            /**********************************************************************************************************************
             * 
             * @param policyPath
             * @return
             * @throws UnknownResourceException
             */
            @RequestMapping(value = "/{policyPath}", method = RequestMethod.GET)
            public Publication getByPolicyPath(@PathVariable String policyPath) throws UnknownResourceException{
                logger.debug("policyPath=" + policyPath);
    
                Publication ret = publicationsDAO.getPublication(policyPath);
    
                HashMap<String, Object> map = new HashMap<String, Object>();
                map.put("TEST1", null);
                map.put("TEST2", new Integer(101));
                map.put("TEST3", "QuinnZilla");
                map.put("TEST4", new DateTime());
                ret.setTokens(map);
                return ret;
    
            }
    
    }
    

    我得到了输出结果

    {
      "tokens": {
        "TEST2": 101,
        "TEST3": "QuinnZilla",
        "TEST4": "2015-10-06T16:59:35.120Z",
        "TEST1": null
      },
      "href": null,
      "policyPath": null
    }
    

    【讨论】:

    • 所有这些都需要将 JodaTime DateTime 实例转换为 ISO8601 String 吗?不确定 Spring 是否让这变得更容易或更更难 ...
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-08-12
    • 2015-05-02
    相关资源
    最近更新 更多