Bagaimana cara menyesuaikan mapper Jackson JSON yang secara implisit digunakan oleh Spring Boot?

102

Saya menggunakan Spring Boot (1.2.1), dengan cara yang sama seperti tutorial Membangun Layanan Web yang tenang :

@RestController
public class EventController {

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

}

Jadi di atas, Spring MVC secara implisit menggunakan Jackson untuk menserialisasikan EventListobjek saya ke JSON.

Tetapi saya ingin melakukan beberapa penyesuaian sederhana pada format JSON, seperti:

setSerializationInclusion(JsonInclude.Include.NON_NULL)

Pertanyaannya adalah, apa cara termudah untuk menyesuaikan mapper JSON implisit?

Saya mencoba pendekatan tersebut dalam posting blog ini , membuat CustomObjectMapper dan seterusnya, tetapi langkah 3, "Daftarkan kelas dalam konteks Musim Semi", gagal:

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

Sepertinya instruksi tersebut untuk versi Spring MVC, sementara saya mencari cara sederhana untuk membuatnya berfungsi dengan Spring Boot terbaru.

Jonik
sumber
Apakah Anda sudah memasukkan Anotasi ini ?: @SuppressWarnings ({"SpringJavaAutowiringInspection"})
sven.kwiotek
Perhatikan bahwa jika Anda menggunakan Spring Web juga, Anda harus memberitahukannya secara manual untuk menggunakan ObjectMapper ini, jika tidak maka akan membuat instance sendiri yang tidak akan dikonfigurasi. Lihat stackoverflow.com/questions/7854030/…
Constantino Cronemberger

Jawaban:

116

Jika Anda menggunakan Spring Boot 1.3, Anda dapat mengonfigurasi penyertaan serialisasi melalui application.properties:

spring.jackson.serialization-inclusion=non_null

Mengikuti perubahan di Jackson 2.7, Spring Boot 1.4 menggunakan properti bernama spring.jackson.default-property-inclusion:

spring.jackson.default-property-inclusion=non_null

Lihat bagian " Menyesuaikan Jackson ObjectMapper " dalam dokumentasi Spring Boot.

Jika Anda menggunakan Spring Boot versi sebelumnya, cara termudah untuk mengkonfigurasi penyertaan serialisasi di Spring Boot adalah dengan mendeklarasikan Jackson2ObjectMapperBuilderbean Anda sendiri yang dikonfigurasi dengan tepat . Sebagai contoh:

@Bean
public Jackson2ObjectMapperBuilder objectMapperBuilder() {
    Jackson2ObjectMapperBuilder builder = new Jackson2ObjectMapperBuilder();
    builder.serializationInclusion(JsonInclude.Include.NON_NULL);
    return builder;
}
Andy Wilkinson
sumber
1
Baiklah, ini sepertinya berhasil. Di mana Anda akan meletakkan metode seperti itu? Mungkin di kelas Aplikasi utama (dengan @ComponentScan, @EnableAutoConfigurationdll)?
Jonik
2
Iya. Metode ini dapat digunakan di semua @Configurationkelas dalam aplikasi Anda. Kelas utama Applicationadalah tempat yang bagus untuk itu.
Andy Wilkinson
2
Catatan penting: Kelas Jackson2ObjectMapperBuilder adalah bagian dari komponen web pegas dan telah ditambahkan pada versi 4.1.1.
gaoagong
2
@Deprecated setSerializationInclusion
Dimitri Kopriwa
6
Tidak digunakan lagi: ObjectMapper.setSerializationInclusion tidak digunakan lagi di Jackson 2.7 ... gunakan stackoverflow.com/a/44137972/6785908 spring.jackson.default-property-inclusion = non_null sebagai gantinya
so-random-dude
24

Saya menjawab agak terlambat untuk pertanyaan ini, tetapi seseorang, di masa depan, mungkin menganggap ini berguna. Pendekatan di bawah ini, selain banyak pendekatan lain, bekerja paling baik, dan menurut saya pribadi akan lebih sesuai untuk aplikasi 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()));
    }
}
Vijay Veeraraghavan
sumber
1
Dalam versi terbaru Anda harus menerapkanWebMvcConfigurer
João Pedro Schmitt
ya, teman-teman, coba solusi ini, saya sudah mencoba menyediakan Bean untuk ObjectMapper, kemudian Bean untuk Jackson2ObjectMapperBuilder membeli yang tidak berhasil dan saya masih tidak tahu mengapa. Menambahkan coverter berhasil!
Somal Somalski
23

Banyak hal yang dapat dikonfigurasi di properti aplikasi. Sayangnya fitur ini hanya ada di Versi 1.3, tetapi Anda dapat menambahkan di Config-Class

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

[PEMBARUAN: Anda harus bekerja pada ObjectMapper karena build()-metode dipanggil sebelum konfigurasi dijalankan.]

nbank
sumber
Itulah solusi yang menyelamatkan hari saya. Kecuali saya menambahkan metode ini ke pengontrol REST itu sendiri, daripada ke kelas konfigurasi.
Nestor Milyaev
21

Dokumentasi menyatakan beberapa cara untuk melakukan ini.

Jika Anda ingin mengganti default ObjectMappersepenuhnya, tentukan @Beanjenis itu dan tandai sebagai @Primary.

Mendefinisikan @Beantipe Jackson2ObjectMapperBuilderakan memungkinkan Anda untuk menyesuaikan default ObjectMapperdan XmlMapper(digunakan dalam MappingJackson2HttpMessageConverterdan MappingJackson2XmlHttpMessageConvertermasing - masing).

Predrag Maric
sumber
2
Bagaimana melakukan hal yang sama tanpa mengganti default ObjectMapper? Maksud saya, menjaga default dan juga kebiasaan.
soufrk
12

Anda dapat menambahkan metode berikut di dalam kelas bootstrap Anda yang dianotasi dengan @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;
}
sendon1982
sumber
Yang ini berfungsi untuk saya menggunakan Boot 2.1.3. Properti spring.jackson tidak berpengaruh.
Richtopia
9

spring.jackson.serialization-inclusion=non_null dulu bekerja untuk kami

Tetapi ketika kami meningkatkan versi boot musim semi ke 1.4.2.RELEASE atau lebih tinggi, itu berhenti bekerja.

Sekarang, properti lain spring.jackson.default-property-inclusion=non_nullsedang melakukan keajaiban.

faktanya, serialization-inclusionsudah usang. Inilah yang dilemparkan intellij saya kepada saya.

Deprecated: ObjectMapper.setSerializationInclusion tidak digunakan lagi di Jackson 2.7

Jadi, mulailah menggunakan spring.jackson.default-property-inclusion=non_nullsebagai gantinya

begitu-acak-bung
sumber
4

Saya menemukan solusi lain, yang cukup bagus.

Pada dasarnya, hanya lakukan langkah 2 dari blog yang diposting yang disebutkan , dan tentukan ObjectMapper kustom sebagai Spring @Component. (Hal-hal mulai bekerja ketika saya baru saja menghapus semua hal AnnotationMethodHandlerAdapter dari langkah 3.)

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

Bekerja selama komponen ada dalam paket yang dipindai oleh Spring. (Menggunakan @Primarytidak wajib dalam kasus saya, tetapi mengapa tidak membuatnya eksplisit.)

Bagi saya ada dua keuntungan dibandingkan dengan pendekatan lainnya:

  • Ini lebih sederhana; Saya hanya dapat memperpanjang kelas dari Jackson dan tidak perlu tahu tentang hal-hal yang sangat spesifik untuk Musim Semi Jackson2ObjectMapperBuilder.
  • Saya ingin menggunakan konfigurasi Jackson yang sama untuk men-deserialisasikan JSON di bagian lain aplikasi saya, dan cara ini sangat sederhana: new CustomObjectMapper()alih-alih new ObjectMapper().
Jonik
sumber
Kelemahan dari pendekatan ini adalah bahwa konfigurasi kustom Anda tidak akan diterapkan ke ObjectMapperinstance apa pun yang dibuat atau dikonfigurasi oleh Spring Boot.
Andy Wilkinson
Hmm, konfigurasi kustom yang digunakan untuk serialisasi tersirat dalam @RestControllerkelas yang saat ini sudah cukup bagi saya. (Jadi maksud Anda, instance tersebut dibuat oleh Spring MVC, bukan Spring Boot?) Tetapi jika saya mengalami kasus lain di mana ObjectMappers perlu dibuatkan, saya akan mengingat pendekatan Jackson2ObjectMapperBuilder!
Jonik
3

Ketika saya mencoba menjadikan ObjectMapper sebagai yang utama di boot musim semi 2.0.6 Saya mendapat kesalahan Jadi saya memodifikasi yang dibuat oleh sepatu bot musim semi untuk saya

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

Saya menemukan solusi yang dijelaskan di atas dengan:

spring.jackson.serialization-inclusion = non_null

Untuk hanya bekerja mulai dari boot musim semi versi 1.4.0.RELEASE. Dalam semua kasus lainnya, konfigurasi diabaikan.

Saya memverifikasi ini dengan bereksperimen dengan modifikasi sampel sepatu bot musim semi "spring-boot-sample-jersey"

Tim Dugan
sumber
0

Saya tahu pertanyaan yang menanyakan tentang sepatu bot Musim Semi, tetapi saya yakin banyak orang yang mencari cara melakukan ini di sepatu non-Musim Semi, seperti saya mencari hampir sepanjang hari.

Di atas Spring 4, Anda tidak perlu melakukan konfigurasi MappingJacksonHttpMessageConverterjika Anda hanya bermaksud untuk mengkonfigurasi ObjectMapper.

Anda hanya perlu melakukan:

public class MyObjectMapper extends ObjectMapper {

    private static final long serialVersionUID = 4219938065516862637L;

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

Dan dalam konfigurasi Spring Anda, buat kacang ini:

@Bean 
public MyObjectMapper myObjectMapper() {        
    return new MyObjectMapper();
}
GMsoF
sumber