Làm cách nào để tùy chỉnh trình ánh xạ Jackson JSON được Spring Boot sử dụng ngầm?


101

Tôi đang sử dụng Spring Boot (1.2.1), theo cách tương tự như trong hướng dẫn Xây dựng Dịch vụ Web RESTful của họ :

@RestController
public class EventController {

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

}

Vì vậy, ở trên, Spring MVC ngầm sử dụng Jackson để tuần tự EventListđối tượng của tôi thành JSON.

Nhưng tôi muốn thực hiện một số tùy chỉnh đơn giản đối với định dạng JSON, chẳng hạn như:

setSerializationInclusion(JsonInclude.Include.NON_NULL)

Câu hỏi là, cách đơn giản nhất để tùy chỉnh trình ánh xạ JSON ngầm định là gì?

Tôi đã thử cách tiếp cận trong bài đăng blog này , tạo CustomObjectMapper, v.v., nhưng bước 3, "Đăng ký lớp trong bối cảnh Spring", không thành công:

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

Có vẻ như những hướng dẫn đó dành cho các phiên bản cũ hơn của Spring MVC, trong khi tôi đang tìm một cách đơn giản để làm cho điều này hoạt động với Spring Boot mới nhất.


Bạn đã chèn Chú thích này chưa ?: @SuppressWarnings ({"SpringJavaAutowiringInspection"})
sven.kwiotek

Lưu ý rằng nếu bạn cũng đang sử dụng Spring Web, bạn sẽ cần yêu cầu nó sử dụng ObjectMapper này theo cách thủ công, nếu không nó sẽ tạo phiên bản riêng của nó mà sẽ không được cấu hình. Xem stackoverflow.com/questions/7854030/…
Constantino Cronemberger,

Câu trả lời:


116

Nếu bạn đang sử dụng Spring Boot 1.3, bạn có thể định cấu hình bao gồm tuần tự hóa thông qua application.properties:

spring.jackson.serialization-inclusion=non_null

Sau những thay đổi trong Jackson 2.7, Spring Boot 1.4 sử dụng thuộc tính có tên spring.jackson.default-property-inclusionthay thế:

spring.jackson.default-property-inclusion=non_null

Xem phần " Tùy chỉnh Jackson ObjectMapper " trong tài liệu Spring Boot.

Nếu bạn đang sử dụng phiên bản Spring Boot cũ hơn, cách dễ nhất để định cấu hình bao gồm tuần tự hóa trong Spring Boot là khai báo Jackson2ObjectMapperBuilderbean được định cấu hình thích hợp của riêng bạn . Ví dụ:

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

1
Được rồi, điều này có vẻ hiệu quả. Bạn sẽ đặt một phương pháp như vậy ở đâu? Có lẽ trong một lớp Ứng dụng chính (với @ComponentScan, @EnableAutoConfigurationv.v.)?
Jonik

2
Đúng. Phương thức này có thể áp dụng cho bất kỳ @Configurationlớp nào trong ứng dụng của bạn. ApplicationLớp học chính là một nơi tốt cho nó.
Andy Wilkinson

2
Lưu ý quan trọng: Lớp Jackson2ObjectMapperBuilder là một phần của thành phần spring-web và đã được thêm vào trong phiên bản 4.1.1.
gaoagong

2
@Deprecated setSerializationInclusion
Dimitri Kopriwa

6
Chấp nhận: ObjectMapper.setSerializationInclusion đã bị phản đối ở Jackson 2,7 ... sử dụng stackoverflow.com/a/44137972/6785908 spring.jackson.default-tài sản bao gồm = non_null thay vì
cái ngẫu nhiên anh chàng

24

Tôi trả lời hơi muộn cho câu hỏi này, nhưng ai đó, trong tương lai, có thể thấy điều này hữu ích. Cách tiếp cận dưới đây, bên cạnh nhiều cách tiếp cận khác, hoạt động tốt nhất và cá nhân tôi nghĩ sẽ phù hợp hơn với ứng dụng 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
Trong phiên bản gần đây, bạn phải triển khaiWebMvcConfigurer
João Pedro Schmitt,

vâng, mọi người, hãy thử giải pháp này, tôi đã cố gắng cung cấp Bean cho ObjectMapper, sau đó mua Bean cho Jackson2ObjectMapperBuilder không hoạt động và tôi vẫn không biết tại sao. Người che phủ ứng dụng đã hoạt động!
Somal Somalski

23

Rất nhiều thứ có thể được cấu hình trong các tính năng ứng dụng. Rất tiếc, tính năng này chỉ có trong Phiên bản 1.3, nhưng bạn có thể thêm vào Lớp cấu hình

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

[CẬP NHẬT: Bạn phải làm việc trên ObjectMapper vì build()-method được gọi trước khi chạy cấu hình.]


Đó là giải pháp đã cứu rỗi một ngày của tôi. Ngoại trừ việc tôi đã thêm phương thức này vào chính bộ điều khiển REST, chứ không phải vào lớp cấu hình.
Nestor Milyaev

20

Tài liệu nêu một số cách để thực hiện việc này.

Nếu bạn muốn thay thế ObjectMapperhoàn toàn kiểu mặc định , hãy xác định @Beankiểu đó và đánh dấu là @Primary.

Việc xác định @Beankiểu Jackson2ObjectMapperBuildersẽ cho phép bạn tùy chỉnh cả mặc định ObjectMapperXmlMapper(được sử dụng trong MappingJackson2HttpMessageConverterMappingJackson2XmlHttpMessageConvertertương ứng).


2
Làm thế nào để làm điều tương tự mà không thay thế mặc định ObjectMapper? Ý tôi là giữ mặc định cũng là một tùy chỉnh.
soufrk

12

Bạn có thể thêm một phương thức sau vào bên trong lớp bootstrap của mình được chú thích bằng @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;
}

Cái này làm việc cho tôi bằng cách sử dụng Boot 2.1.3. Các thuộc tính spring.jackson không có tác dụng.
Richtopia

9

spring.jackson.serialization-inclusion=non_null từng làm việc cho chúng tôi

Nhưng khi chúng tôi nâng cấp phiên bản khởi động mùa xuân lên 1.4.2.RELEASE hoặc cao hơn, nó ngừng hoạt động.

Bây giờ, một tài sản khác spring.jackson.default-property-inclusion=non_nullđang làm điều kỳ diệu.

trên thực tế, serialization-inclusionkhông được dùng nữa. Đây là những gì intellij của tôi ném vào tôi.

Không được chấp nhận: ObjectMapper.setSerializationInclusion không được chấp nhận trong Jackson 2.7

Vì vậy, hãy bắt đầu sử dụng spring.jackson.default-property-inclusion=non_nullthay thế


4

Tôi tình cờ tìm ra một giải pháp khác, khá hay.

Về cơ bản, chỉ thực hiện bước 2 từ blog đã đăng được đề cập và xác định một ObjectMapper tùy chỉnh làm Spring @Component. (Mọi thứ bắt đầu hoạt động khi tôi vừa xóa tất cả nội dung AnnotationMethodHandlerAdapter từ bước 3)

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

Hoạt động miễn là thành phần nằm trong một gói được quét bởi Spring. ( @PrimaryTrong trường hợp của tôi, việc sử dụng là không bắt buộc, nhưng tại sao không làm cho mọi thứ rõ ràng.)

Đối với tôi, có hai lợi ích so với cách tiếp cận khác:

  • Điều này đơn giản hơn; Tôi chỉ có thể mở rộng một lớp học từ Jackson và không cần biết về những thứ quá cụ thể về mùa xuân như Jackson2ObjectMapperBuilder.
  • Tôi muốn sử dụng cùng một cấu hình Jackson để deserialising JSON trong một phần khác của ứng dụng của tôi và theo cách này, nó rất đơn giản: new CustomObjectMapper()thay vì new ObjectMapper().

Nhược điểm của phương pháp này là cấu hình tùy chỉnh của bạn sẽ không được áp dụng cho bất kỳ ObjectMappertrường hợp nào được tạo hoặc cấu hình bởi Spring Boot.
Andy Wilkinson

Rất tiếc, cấu hình tùy chỉnh được sử dụng để tuần tự hóa ngầm định trong @RestControllercác lớp mà hiện tại là đủ đối với tôi. (Ý bạn là những trường hợp đó được tạo bởi Spring MVC chứ không phải Spring Boot?) Nhưng nếu tôi gặp các trường hợp khác mà ObjectMappers cần phải được khởi tạo, tôi sẽ lưu ý phương pháp Jackson2ObjectMapperBuilder!
Jonik

3

Khi tôi cố gắng đặt ObjectMapper chính trong khởi động mùa xuân 2.0.6, tôi gặp lỗi Vì vậy, tôi đã sửa đổi một chương trình khởi động mùa xuân đã tạo cho tôi

Cũng xem 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

Tôi đã tìm thấy giải pháp được mô tả ở trên với:

spring.jackson.serialization-inclusive = non_null

Để chỉ hoạt động bắt đầu từ phiên bản 1.4.0.RELEASE của khởi động mùa xuân. Trong tất cả các trường hợp khác, cấu hình bị bỏ qua.

Tôi đã xác minh điều này bằng cách thử nghiệm với một sửa đổi của mẫu khởi động mùa xuân "spring-boot-sample-jersey"


0

Tôi biết câu hỏi yêu cầu khởi động Spring, nhưng tôi tin rằng rất nhiều người đang tìm cách thực hiện điều này trong khởi động không phải Spring, giống như tôi đã tìm kiếm gần như cả ngày.

Trên Spring 4, không cần cấu hình MappingJacksonHttpMessageConverternếu bạn chỉ định cấu hình ObjectMapper.

Bạn chỉ cần làm:

public class MyObjectMapper extends ObjectMapper {

    private static final long serialVersionUID = 4219938065516862637L;

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

Và trong cấu hình Spring của bạn, hãy tạo bean này:

@Bean 
public MyObjectMapper myObjectMapper() {        
    return new MyObjectMapper();
}
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.