Come personalizzare il mapper Jackson JSON utilizzato implicitamente da Spring Boot?


101

Sto usando Spring Boot (1.2.1), in modo simile al tutorial sulla creazione di un servizio Web RESTful :

@RestController
public class EventController {

    @RequestMapping("/events/all")
    EventList events() {
        return proxyService.getAllEvents();
    }

}

Quindi, sopra, Spring MVC utilizza implicitamente Jackson per serializzare il mio EventListoggetto in JSON.

Ma voglio fare alcune semplici personalizzazioni al formato JSON, come ad esempio:

setSerializationInclusion(JsonInclude.Include.NON_NULL)

La domanda è: qual è il modo più semplice per personalizzare il mapper JSON implicito?

Ho provato l'approccio in questo post del blog , creando un CustomObjectMapper e così via, ma il passaggio 3, "Registra classi nel contesto Spring", non riesce:

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: {}

Sembra che queste istruzioni siano per le versioni precedenti di Spring MVC, mentre sto cercando un modo semplice per farlo funzionare con l'ultimo Spring Boot.


Hai inserito questa annotazione ?: @SuppressWarnings ({"SpringJavaAutowiringInspection"})
sven.kwiotek

Notare che se si utilizza anche Spring Web, sarà necessario dirgli manualmente di utilizzare questo ObjectMapper altrimenti creerà una propria istanza che non verrà configurata. Vedere stackoverflow.com/questions/7854030/...
Constantino Cronemberger

Risposte:


116

Se stai usando Spring Boot 1.3, puoi configurare l'inclusione della serializzazione tramite application.properties:

spring.jackson.serialization-inclusion=non_null

A seguito delle modifiche in Jackson 2.7, Spring Boot 1.4 utilizza invece una proprietà denominata spring.jackson.default-property-inclusion:

spring.jackson.default-property-inclusion=non_null

Vedere la sezione " Personalizzare Jackson ObjectMapper " nella documentazione di Spring Boot.

Se stai usando una versione precedente di Spring Boot, il modo più semplice per configurare l'inclusione della serializzazione in Spring Boot è dichiarare il tuo Jackson2ObjectMapperBuilderbean opportunamente configurato . Per esempio:

@Bean
public Jackson2ObjectMapperBuilder objectMapperBuilder() {
    Jackson2ObjectMapperBuilder builder = new Jackson2ObjectMapperBuilder();
    builder.serializationInclusion(JsonInclude.Include.NON_NULL);
    return builder;
}

1
Va bene, sembra funzionare. Dove metteresti un tale metodo? Forse in una classe principale dell'applicazione (con @ComponentScan, @EnableAutoConfigurationecc.)?
Jonik

2
Sì. Questo metodo può essere utilizzato in qualsiasi @Configurationclasse nella tua app. La Applicationclasse principale è un buon posto per farlo.
Andy Wilkinson

2
Nota importante: la classe Jackson2ObjectMapperBuilder fa parte del componente spring-web ed è stata aggiunta nella versione 4.1.1.
gaoagong

2
@Deprecated setSerializationInclusion
Dimitri Kopriwa

6
Deprecato: ObjectMapper.setSerializationInclusion è stata sconsigliata a Jackson 2.7 ... uso stackoverflow.com/a/44137972/6785908 spring.jackson.default-proprietà-inclusione = non_null invece
in modo casuale-dude

24

Rispondo un po 'tardi a questa domanda, ma qualcuno, in futuro, potrebbe trovarlo utile. L'approccio seguente, oltre a molti altri approcci, funziona meglio e personalmente penso che sarebbe più adatto a un'applicazione 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()));
    }
}

1
Nella versione recente è necessario implementareWebMvcConfigurer
João Pedro Schmitt il

sì, gente, prova questa soluzione, ho provato a fornire Bean per ObjectMapper, quindi Bean per Jackson2ObjectMapperBuilder acquista che non ha funzionato e non ho ancora idea del perché. L'aggiunta di coverter ha funzionato!
Somal Somalski

23

Molte cose possono essere configurate nelle proprietà dell'applicazione. Sfortunatamente questa funzione è solo nella versione 1.3, ma puoi aggiungerla in un Config-Class

@Autowired(required = true)
public void configureJackson(ObjectMapper jackson2ObjectMapper) {
    jackson2ObjectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
}

[AGGIORNAMENTO: è necessario lavorare su ObjectMapper perché il build()-method viene chiamato prima che venga eseguita la configurazione.]


Questa è stata la soluzione che mi ha salvato la giornata. Tranne che ho aggiunto questo metodo al controller REST stesso, piuttosto che alla classe di configurazione.
Nestor Milyaev il

20

La documentazione indica diversi modi per farlo.

Se vuoi sostituire ObjectMappercompletamente il valore predefinito , definisci un @Beandi quel tipo e contrassegnalo come @Primary.

La definizione di un @Beantipo Jackson2ObjectMapperBuilderti consentirà di personalizzare sia predefinito ObjectMapperche XmlMapper(utilizzato rispettivamente in MappingJackson2HttpMessageConvertere MappingJackson2XmlHttpMessageConverter).


2
Come fare lo stesso senza sostituire l'impostazione predefinita ObjectMapper? Intendo mantenere anche l'impostazione predefinita un'abitudine.
soufrk

12

Puoi aggiungere un metodo seguente all'interno della tua classe bootstrap che è annotato con @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;
}

Questo ha funzionato per me usando Boot 2.1.3. Le proprietà spring.jackson non hanno avuto effetto.
Richtopia

9

spring.jackson.serialization-inclusion=non_null lavorava per noi

Ma quando abbiamo aggiornato la versione di avvio a molla a 1.4.2.RELEASE o superiore, ha smesso di funzionare.

Ora, un'altra proprietà spring.jackson.default-property-inclusion=non_nullsta facendo la magia.

infatti, serialization-inclusionè deprecato. Questo è ciò che il mio intellij mi lancia.

Deprecato: ObjectMapper.setSerializationInclusion è stato deprecato in Jackson 2.7

Quindi, inizia a usare spring.jackson.default-property-inclusion=non_nullinvece


4

Mi sono imbattuto in un'altra soluzione, che è piuttosto carina.

Fondamentalmente, esegui solo il passaggio 2 dal blog pubblicato menzionato e definisci un ObjectMapper personalizzato come Spring @Component. (Le cose hanno iniziato a funzionare quando ho appena rimosso tutto il materiale AnnotationMethodHandlerAdapter dal passaggio 3.)

@Component
@Primary
public class CustomObjectMapper extends ObjectMapper {
    public CustomObjectMapper() {
        setSerializationInclusion(JsonInclude.Include.NON_NULL); 
        configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); 
    }
}

Funziona finché il componente si trova in un pacchetto scansionato da Spring. (L'uso @Primarynon è obbligatorio nel mio caso, ma perché non rendere le cose esplicite.)

Per me ci sono due vantaggi rispetto all'altro approccio:

  • Questo è più semplice; Posso semplicemente estendere una lezione da Jackson e non ho bisogno di sapere di cose altamente specifiche per la primavera come Jackson2ObjectMapperBuilder.
  • Voglio utilizzare le stesse configurazioni di Jackson per deserializzare JSON in un'altra parte della mia app, e in questo modo è molto semplice: new CustomObjectMapper()invece di new ObjectMapper().

Lo svantaggio di questo approccio è che la configurazione personalizzata non verrà applicata a nessuna ObjectMapperistanza creata o configurata da Spring Boot.
Andy Wilkinson

Hmm, la configurazione personalizzata viene utilizzata per la serializzazione implicita nelle @RestControllerclassi che attualmente è sufficiente per me. (Quindi intendi che quelle istanze sono create da Spring MVC, non da Spring Boot?) Ma se mi imbatto in altri casi in cui è necessario istanziare ObjectMappers, terrò a mente l'approccio Jackson2ObjectMapperBuilder!
Jonik

3

Quando ho provato a rendere ObjectMapper primario nell'avvio primaverile 2.0.6 ho ricevuto errori Quindi ho modificato quello che lo stivale primaverile ha creato per me

Vedi anche 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

Ho trovato la soluzione sopra descritta con:

spring.jackson.serialization-included = non_null

Per funzionare solo a partire dalla versione 1.4.0.RELEASE dello stivale a molla. In tutti gli altri casi la configurazione viene ignorata.

L'ho verificato sperimentando una modifica del campione di stivali primaverili "spring-boot-sample-jersey"


0

Conosco la domanda che chiede lo stivale primaverile, ma credo che molte persone cerchino come farlo in stivale non primaverile, come me che cercano quasi tutto il giorno.

Sopra Spring 4, non è necessario configurare MappingJacksonHttpMessageConverterse intendi solo configurare ObjectMapper.

Devi solo fare:

public class MyObjectMapper extends ObjectMapper {

    private static final long serialVersionUID = 4219938065516862637L;

    public MyObjectMapper() {
        super();
        enable(SerializationFeature.INDENT_OUTPUT);
    }       
}

E nella tua configurazione Spring, crea questo bean:

@Bean 
public MyObjectMapper myObjectMapper() {        
    return new MyObjectMapper();
}
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.