web-dev-qa-db-fra.com

Formatage automatique Jackson de Joda DateTime au format ISO 8601

Selon http://wiki.fasterxml.com/JacksonFAQDateHandling , "DateTime peut être automatiquement sérialisé/désérialisé de la même manière que Java.util.Date est géré. "Cependant, je ne suis pas en mesure d'accomplir cette fonctionnalité automatique . Il y a des discussions StackOverflow liées à ce sujet, mais la plupart impliquent une solution basée sur du code, mais sur la base de la citation ci-dessus, je devrais être en mesure de le faire via une configuration simple.

Per http://wiki.fasterxml.com/JacksonFAQDateHandling J'ai ma configuration définie de sorte que l'écriture des dates sous forme d'horodatages est fausse. Le résultat est que les types Java.util.Date sont sérialisés au format ISO 8601, mais les types org.joda.time.DateTime sont sérialisés en une longue représentation d'objet.

Mon environnement est le suivant:

Jackson 2.1
Temps Joda 2.1
Printemps 3.2
Java 1.6

Ma configuration Spring pour le bean jsonMapper est

@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;
}

Mon extrait de code de test est le suivant

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);

L'extrait de sortie JSON résultant est le suivant:

{"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*}}

Mon attente est que l'objet DateTime doit correspondre à celui de l'objet Date en fonction de la configuration. Qu'est-ce que je fais mal ou qu'est-ce que je comprends mal? Suis-je en train de lire trop dans le Word automatiquement à partir de la documentation de Jackson et le fait qu'une représentation de chaîne a été produite, bien qu'ISO 8601, produit la publication fonctionnalité automatique?

36
Jeff M

J'ai pu obtenir la réponse à cela à partir de la liste de diffusion des utilisateurs de Jackson et je voulais partager avec vous car c'est un problème de débutant. En lisant la FAQ Jackson Date, je ne me suis pas rendu compte que des dépendances et un enregistrement supplémentaires sont nécessaires, mais c'est le cas. Il est documenté sur la page du projet git hub ici https://github.com/FasterXML/jackson-datatype-joda

Essentiellement, j'ai dû ajouter une autre dépendance à un pot Jackson spécifique au type de données Joda, puis j'ai dû enregistrer l'utilisation de ce module sur le mappeur d'objets. Les extraits de code sont ci-dessous.

Pour ma configuration de dépendance Maven du type de données Jackson Joda, j'ai utilisé ceci:

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

Pour enregistrer la fonctionnalité de sérialisation/désérialisation Joda, j'ai utilisé ceci:

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

Utilisation de Spring Boot.

Ajoutez à votre configuration Maven ...

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

Puis à votre 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);
    }
}
9
kervin

Dans Spring Boot, la configuration est encore plus simple. Vous déclarez simplement la dépendance Maven

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

puis ajoutez le paramètre de configuration à votre fichier application.yml/properties:

spring.jackson.serialization.write-dates-as-timestamps: false
3
Igor Bljahhin

Je pensais publier un exemple de travail mis à jour en utilisant: 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>

Code 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;

        }

}

Et j'obtiens le résultat de sortie

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