web-dev-qa-db-fra.com

Comment personnaliser le mappeur Jackson JSON utilisé implicitement par Spring Boot?

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.

82
Jonik

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;
}
99
Andy Wilkinson

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.]

20
nbank

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()));
    }
}
20

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 type Jackson2ObjectMapperBuilder vous permettra de personnaliser les valeurs par défaut ObjectMapper et XmlMapper (utilisées dans MappingJackson2HttpMessageConverter et MappingJackson2XmlHttpMessageConverter, respectivement).

18
Predrag Maric

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

9
so-random-dude

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

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:

  • C'est plus simple. Je peux juste prolonger une classe de Jackson et je n'ai pas besoin de connaître des choses très spécifiques à Spring, comme Jackson2ObjectMapperBuilder.
  • Je souhaite utiliser les mêmes configurations Jackson pour la désérialisation de JSON dans une autre partie de mon application. Cette procédure est donc très simple: new CustomObjectMapper() au lieu de new ObjectMapper().
4
Jonik

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;
}
1
Kalpesh Soni

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".

0
Tim Dugan

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();
}
0
GMsoF