J'utilise Spring Boot (1.2.1), de la même manière que dans leur Création d'un service Web RESTful :
@RestController
public class EventController {
@RequestMapping("/events/all")
EventList events() {
return proxyService.getAllEvents();
}
}
Donc, plus haut, Spring MVC utilise implicitement Jackson pour la sérialisation de mon objet EventList
en JSON.
Mais je veux faire quelques personnalisations simples au format JSON, telles que:
setSerializationInclusion(JsonInclude.Include.NON_NULL)
La question est quel est le moyen le plus simple de personnaliser le mappeur JSON implicite?
J'ai essayé l'approche dans cet article de blog, en créant un CustomObjectMapper, etc., mais à l'étape 3, "Enregistrer des classes dans le contexte Spring ", échoue:
org.springframework.beans.factory.BeanCreationException:
Error creating bean with name 'jacksonFix': Injection of autowired dependencies failed;
nested exception is org.springframework.beans.factory.BeanCreationException:
Could not autowire method: public void com.acme.project.JacksonFix.setAnnotationMethodHandlerAdapter(org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter);
nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException:
No qualifying bean of type [org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter]
found for dependency: expected at least 1 bean which qualifies as autowire candidate for this dependency. Dependency annotations: {}
Il semble que ces instructions s’appliquent aux anciennes versions de Spring MVC, alors que je cherche un moyen simple de faire fonctionner ce dernier avec la dernière version de Spring Boot.
Si vous utilisez Spring Boot 1.3, vous pouvez configurer l'inclusion de la sérialisation via application.properties
:
spring.jackson.serialization-inclusion=non_null
Suite aux modifications apportées à Jackson 2.7, Spring Boot 1.4 utilise à la place une propriété appelée spring.jackson.default-property-inclusion
:
spring.jackson.default-property-inclusion=non_null
Voir la section " Personnalisation de Jackson ObjectMapper " dans la documentation de Spring Boot.
Si vous utilisez une version antérieure de Spring Boot, le moyen le plus simple de configurer l'inclusion de la sérialisation dans Spring Boot consiste à déclarer votre propre bean Jackson2ObjectMapperBuilder
correctement configuré. Par exemple:
@Bean
public Jackson2ObjectMapperBuilder objectMapperBuilder() {
Jackson2ObjectMapperBuilder builder = new Jackson2ObjectMapperBuilder();
builder.serializationInclusion(JsonInclude.Include.NON_NULL);
return builder;
}
Beaucoup de choses peuvent être configurées dans applicationproperties. Malheureusement, cette fonctionnalité est disponible uniquement dans la version 1.3, mais vous pouvez ajouter une classe de configuration.
@Autowired(required = true)
public void configureJackson(ObjectMapper jackson2ObjectMapper) {
jackson2ObjectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
}
[UPDATE: vous devez travailler sur ObjectMapper car la méthode build()
- est appelée avant l'exécution de la configuration.]
Je réponds un peu tard à cette question, mais quelqu'un, à l'avenir, pourrait trouver cela utile. L'approche ci-dessous, outre de nombreuses autres approches, fonctionne le mieux et je pense personnellement que cela conviendrait mieux à une application Web.
@Configuration
@EnableWebMvc
public class WebConfiguration extends WebMvcConfigurerAdapter {
... other configurations
@Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
Jackson2ObjectMapperBuilder builder = new Jackson2ObjectMapperBuilder();
builder.serializationInclusion(JsonInclude.Include.NON_NULL);
builder.propertyNamingStrategy(PropertyNamingStrategy.CAMEL_CASE_TO_LOWER_CASE_WITH_UNDERSCORES);
builder.serializationInclusion(Include.NON_EMPTY);
builder.indentOutput(true).dateFormat(new SimpleDateFormat("yyyy-MM-dd"));
converters.add(new MappingJackson2HttpMessageConverter(builder.build()));
converters.add(new MappingJackson2XmlHttpMessageConverter(builder.createXmlMapper(true).build()));
}
}
La documentation indique plusieurs manières de procéder.
Si vous souhaitez remplacer complètement la valeur par défaut
ObjectMapper
, définissez un@Bean
de ce type et marquez-le comme@Primary
.Définir un
@Bean
de typeJackson2ObjectMapperBuilder
vous permettra de personnaliser les valeurs par défautObjectMapper
etXmlMapper
(utilisées dansMappingJackson2HttpMessageConverter
etMappingJackson2XmlHttpMessageConverter
, respectivement).
spring.jackson.serialization-inclusion=non_null
travaillait pour nous
Mais lorsque nous avons mis à niveau la version de démarrage printanière vers la version 1.4.2.RELEASE ou une version ultérieure, elle ne fonctionnait plus.
Maintenant, une autre propriété spring.jackson.default-property-inclusion=non_null
fait la magie.
en fait, serialization-inclusion
est obsolète. C'est ce que mon intellij me lance.
Obsolète: ObjectMapper.setSerializationInclusion est déconseillé dans Jackson 2.7.
Alors, commencez à utiliser spring.jackson.default-property-inclusion=non_null
à la place
Vous pouvez ajouter une méthode suivante dans votre classe bootstrap annotée avec @SpringBootApplication
@Bean
@Primary
public ObjectMapper objectMapper(Jackson2ObjectMapperBuilder builder) {
ObjectMapper objectMapper = builder.createXmlMapper(false).build();
objectMapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
objectMapper.configure(SerializationFeature.WRITE_DATE_TIMESTAMPS_AS_NANOSECONDS, false);
objectMapper.registerModule(new JodaModule());
return objectMapper;
}
Je suis tombé sur une autre solution, qui est assez Nice.
Fondamentalement, ne faites que étape 2 du blog mentionné , et définissez un ObjectMapper personnalisé en tant que Spring @Component
. (Les choses ont commencé à fonctionner lorsque je viens de retirer tous les trucs AnnotationMethodHandlerAdapter de l'étape 3.)
@Component
@Primary
public class CustomObjectMapper extends ObjectMapper {
public CustomObjectMapper() {
setSerializationInclusion(JsonInclude.Include.NON_NULL);
configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
}
}
Fonctionne tant que le composant est dans un package analysé par Spring. (Utiliser @Primary
n'est pas obligatoire dans mon cas, mais pourquoi ne pas rendre les choses explicites.)
Pour moi, il y a deux avantages par rapport à l'autre approche:
Jackson2ObjectMapperBuilder
.new CustomObjectMapper()
au lieu de new ObjectMapper()
.Lorsque j'ai essayé de rendre ObjectMapper primaire dans Spring Boot 2.0.6, j'ai eu des erreurs. J'ai donc modifié celle créée par Spring Boot pour moi.
Voir aussi https://stackoverflow.com/a/48519868/255139
@Lazy
@Autowired
ObjectMapper mapper;
@PostConstruct
public ObjectMapper configureMapper() {
mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
mapper.enable(DeserializationFeature.ACCEPT_EMPTY_STRING_AS_NULL_OBJECT);
mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
mapper.configure(SerializationFeature.ORDER_MAP_ENTRIES_BY_KEYS, true);
mapper.configure(MapperFeature.ALLOW_COERCION_OF_SCALARS, true);
mapper.configure(MapperFeature.SORT_PROPERTIES_ALPHABETICALLY, true);
SimpleModule module = new SimpleModule();
module.addDeserializer(LocalDate.class, new LocalDateDeserializer());
module.addSerializer(LocalDate.class, new LocalDateSerializer());
mapper.registerModule(module);
return mapper;
}
J'ai trouvé la solution décrite ci-dessus avec:
spring.jackson.serialization-inclusion = non_null
Ne fonctionne qu'à partir de la version 1.4.0.RELEASE de Spring Boot. Dans tous les autres cas, la configuration est ignorée.
J'ai vérifié cela en expérimentant une modification de l'échantillon de bottes de printemps "spring-boot-sample-jersey".
Je connais la question concernant les bottes de printemps, mais je crois que beaucoup de gens cherchent comment faire cela dans des bottes de printemps, comme moi, cherchant presque toute la journée.
Au-dessus du printemps 4, il n'est pas nécessaire de configurer MappingJacksonHttpMessageConverter
si vous souhaitez uniquement configurer ObjectMapper
.
Vous avez juste besoin de faire:
public class MyObjectMapper extends ObjectMapper {
private static final long serialVersionUID = 4219938065516862637L;
public MyObjectMapper() {
super();
enable(SerializationFeature.INDENT_OUTPUT);
}
}
Et dans votre configuration Spring, créez ce bean:
@Bean
public MyObjectMapper myObjectMapper() {
return new MyObjectMapper();
}