Jak dostosować program mapujący Jackson JSON niejawnie używany przez Spring Boot?


102

Używam Spring Boot (1.2.1), w podobny sposób, jak w ich samouczku Tworzenie usługi sieci Web RESTful :

@RestController
public class EventController {

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

}

Tak więc powyżej, Spring MVC niejawnie używa Jacksona do serializacji mojego EventListobiektu do formatu JSON.

Ale chcę zrobić kilka prostych dostosowań do formatu JSON, na przykład:

setSerializationInclusion(JsonInclude.Include.NON_NULL)

Pytanie brzmi, jaki jest najprostszy sposób dostosowania niejawnego mapowania JSON?

Wypróbowałem podejście opisane w tym poście na blogu , tworząc CustomObjectMapper i tak dalej, ale krok 3, „Rejestracja klas w kontekście Spring”, kończy się niepowodzeniem:

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

Wygląda na to, że te instrukcje dotyczą starszych wersji Spring MVC, a ja szukam prostego sposobu, aby to działało z najnowszym Spring Boot.


Czy wstawiłeś tę adnotację ?: @SuppressWarnings ({"SpringJavaAutowiringInspection"})
— sven.kwiotek,

Zauważ, że jeśli używasz również Spring Web, będziesz musiał ręcznie powiedzieć mu, aby używał tego ObjectMapper, w przeciwnym razie utworzy własną instancję, która nie zostanie skonfigurowana. Zobacz stackoverflow.com/questions/7854030/…
— Constantino Cronemberger

Odpowiedzi:


117

Jeśli używasz Spring Boot 1.3, możesz skonfigurować włączenie serializacji poprzez application.properties:

spring.jackson.serialization-inclusion=non_null

Po zmianach w Jackson 2.7, Spring Boot 1.4 używa właściwości o nazwie spring.jackson.default-property-inclusion:

spring.jackson.default-property-inclusion=non_null

Zobacz sekcję „ Dostosuj Jackson ObjectMapper ” w dokumentacji Spring Boot.

Jeśli używasz wcześniejszej wersji Spring Boot, najłatwiejszym sposobem skonfigurowania włączenia serializacji do Spring Boot jest zadeklarowanie własnego, odpowiednio skonfigurowanego Jackson2ObjectMapperBuilderkomponentu bean. Na przykład:

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

1
W porządku, to wydaje się działać. Gdzie umieściłbyś taką metodę? Być może w głównej klasie Application (z @ComponentScan, @EnableAutoConfigurationetc)?
— Jonik

2
Tak. Ta metoda może być stosowana w dowolnej @Configurationklasie w Twojej aplikacji. ApplicationKlasa główna jest do tego dobrym miejscem.
— Andy Wilkinson

2
Ważna uwaga: Klasa Jackson2ObjectMapperBuilder jest częścią komponentu spring-web i została dodana w wersji 4.1.1.
— gaoagong

2
@Deprecated setSerializationInclusion
— Dimitri Kopriwa

6
Przestarzałe: ObjectMapper.setSerializationInclusion został wycofany w Jackson 2.7 ... użyj stackoverflow.com/a/44137972/6785908 spring.jackson.default-property-Włączenie = non_null zamiast tego
— so-random-dude

24

Trochę późno odpowiadam na to pytanie, ale ktoś w przyszłości może uznać to za przydatne. Poniższe podejście, oprócz wielu innych podejść, działa najlepiej i osobiście uważam, że lepiej pasowałoby do aplikacji internetowej.

@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
W najnowszej wersji musisz wdrożyćWebMvcConfigurer
— João Pedro Schmitt

tak, ludzie, wypróbuj to rozwiązanie, próbowałem dostarczyć Bean dla ObjectMapper, a następnie Bean dla kupna Jackson2ObjectMapperBuilder, który nie zadziałał i nadal nie mam pojęcia, dlaczego. Dołączanie osłony zadziałało!
— Somal Somalski

23

Wiele rzeczy można skonfigurować we właściwościach aplikacji. Niestety ta funkcja jest dostępna tylko w wersji 1.3, ale można dodać ją w klasie Config

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

[AKTUALIZACJA: Musisz pracować na ObjectMapper, ponieważ build()-method jest wywoływana przed uruchomieniem konfiguracji.]


To było rozwiązanie, które uratowało mi dzień. Tyle że dodałem tę metodę do samego kontrolera REST, a nie do klasy konfiguracji.
— Nestor Milyaev

21

Dokumentacja podaje kilka sposobów, aby to zrobić.

Jeśli chcesz ObjectMappercałkowicie zastąpić domyślne ustawienie , zdefiniuj @Beanten typ i oznacz go jako @Primary.

Zdefiniowanie @Beantypu Jackson2ObjectMapperBuilderpozwoli ci dostosować zarówno domyślne, jak ObjectMapperi XmlMapper(używane odpowiednio w MappingJackson2HttpMessageConverteri MappingJackson2XmlHttpMessageConverter).


2
Jak zrobić to samo bez wymiany domyślnej ObjectMapper? Mam na myśli zachowanie wartości domyślnej, jak również zwyczaju.
— suk

13

Możesz dodać następującą metodę wewnątrz swojej klasy bootstrap, która jest opatrzona adnotacją @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;
}

Ten działał dla mnie przy użyciu Boot 2.1.3. Właściwości spring.jackson nie przyniosły żadnego efektu.
— Richtopia

9

spring.jackson.serialization-inclusion=non_null pracował dla nas

Ale kiedy zaktualizowaliśmy wersję Spring Boot do 1.4.2.RELEASE lub nowszej, przestała działać.

Teraz kolejna nieruchomość spring.jackson.default-property-inclusion=non_null robi magię.

w rzeczywistości serialization-inclusionjest przestarzały. To właśnie rzuca we mnie mój intellij.

Przestarzałe: ObjectMapper.setSerializationInclusion został wycofany w Jackson 2.7

Więc zacznij używać spring.jackson.default-property-inclusion=non_nullzamiast tego


4

Natknąłem się na inne rozwiązanie, które jest całkiem miłe.

Zasadniczo wykonaj tylko krok 2 ze wspomnianego blogu i zdefiniuj niestandardowy obiekt ObjectMapper jako Spring @Component. (Wszystko zaczęło działać, gdy właśnie usunąłem wszystkie elementy AnnotationMethodHandlerAdapter z kroku 3).

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

Działa tak długo, jak długo komponent znajduje się w pakiecie skanowanym przez Spring. (Za pomocą@Primary nie jest obowiązkowe w moim przypadku, ale dlaczego nie wyjaśnić rzeczy).

Dla mnie są dwie korzyści w porównaniu z innym podejściem:

  • To jest prostsze; Mogę po prostu przedłużyć zajęcia z Jacksona i nie muszę wiedzieć o rzeczach związanych z wiosną, takich jak Jackson2ObjectMapperBuilder.
  • Chcę użyć tych samych konfiguracji Jacksona do deserializacji JSON w innej części mojej aplikacji i dzięki temu jest to bardzo proste: new CustomObjectMapper()zamiast new ObjectMapper().

Wadą tego podejścia jest to, że Twoja konfiguracja niestandardowa nie będzie stosowana do żadnych ObjectMapperinstancji, które są tworzone lub konfigurowane przez Spring Boot.
— Andy Wilkinson

Hmm, niestandardowa konfiguracja jest używana do niejawnej serializacji w @RestControllerklasach, które obecnie mi wystarczają. (Więc masz na myśli, że te instancje są tworzone przez Spring MVC, a nie Spring Boot?) Ale jeśli napotkam inne przypadki, w których trzeba utworzyć instancję ObjectMappers, będę pamiętać o podejściu Jackson2ObjectMapperBuilder!
— Jonik

3

Kiedy próbowałem ustawić ObjectMapper jako podstawowy w Spring Boot 2.0.6, otrzymałem błędy Więc zmodyfikowałem ten, który utworzył dla mnie Spring Boot

Zobacz też 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

Znalazłem rozwiązanie opisane powyżej z:

spring.jackson.serialization-inclusive = non_null

Działa tylko od wersji 1.4.0.RELEASE buta sprężynowego. We wszystkich innych przypadkach konfiguracja jest ignorowana.

Sprawdziłem to, eksperymentując z modyfikacją próbki buta sprężynowego „spring-boot-sample-jersey”


0

Znam pytanie dotyczące butów wiosennych, ale wydaje mi się, że wiele osób, które szukają sposobu na zrobienie tego w butach innych niż wiosenne, jak ja, szukam prawie cały dzień.

Powyżej Spring 4 nie ma potrzeby konfigurowania, MappingJacksonHttpMessageConverterjeśli zamierzasz tylko konfigurować ObjectMapper.

Musisz tylko:

public class MyObjectMapper extends ObjectMapper {

    private static final long serialVersionUID = 4219938065516862637L;

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

A w konfiguracji Spring utwórz tę fasolkę:

@Bean 
public MyObjectMapper myObjectMapper() {        
    return new MyObjectMapper();
}
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.