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