program tip

Spring Boot에서 암시 적으로 사용되는 Jackson JSON 매퍼를 사용자 정의하는 방법은 무엇입니까?

radiobox 2020. 9. 16. 07:31
반응형

Spring Boot에서 암시 적으로 사용되는 Jackson JSON 매퍼를 사용자 정의하는 방법은 무엇입니까?


RESTful 웹 서비스 구축 튜토리얼 에서와 비슷한 방식으로 Spring Boot (1.2.1)를 사용하고 있습니다.

@RestController
public class EventController {

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

}

따라서 위의 Spring MVC는 EventListJSON으로 객체 를 직렬화하기 위해 암시 적으로 Jackson을 사용합니다 .

하지만 다음과 같이 JSON 형식에 대한 몇 가지 간단한 사용자 지정을 수행하고 싶습니다.

setSerializationInclusion(JsonInclude.Include.NON_NULL)

질문은 암시 적 JSON 매퍼를 사용자 정의하는 가장 간단한 방법은 무엇입니까?

이 블로그 게시물 에서 CustomObjectMapper 등을 만드는 방법을 시도했지만 3 단계 "Spring 컨텍스트에서 클래스 등록"은 실패합니다.

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

이 지침은 이전 버전의 Spring MVC에 대한 것 같지만 최신 Spring Boot에서이 작업을 수행하는 간단한 방법을 찾고 있습니다.


Spring Boot 1.3을 사용하는 경우 다음을 통해 직렬화 포함을 구성 할 수 있습니다 application.properties.

spring.jackson.serialization-inclusion=non_null

Jackson 2.7의 변경 사항에 따라 Spring Boot 1.4는 spring.jackson.default-property-inclusion대신 이름이 지정된 속성을 사용합니다 .

spring.jackson.default-property-inclusion=non_null

Spring Boot 문서의 " Customize the Jackson ObjectMapper "섹션을 참조하십시오 .

이전 버전의 Spring Boot를 사용하는 경우 Spring Boot에 직렬화 포함을 구성하는 가장 쉬운 방법은 적절하게 구성된 고유 한 Jackson2ObjectMapperBuilderBean 을 선언하는 것 입니다. 예를 들면 :

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

나는이 질문에 조금 늦게 대답하고 있지만, 미래에 누군가가 유용하다고 생각할 것입니다. 아래의 접근 방식은 다른 많은 접근 방식 외에도 가장 잘 작동하며 개인적으로 웹 애플리케이션에 더 적합 할 것이라고 생각합니다.

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

A lot of things can configured in applicationproperties. Unfortunately this feature only in Version 1.3, but you can add in a Config-Class

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

[UPDATE: You must work on the ObjectMapper because the build()-method is called before the config is runs.]


The documentation states several ways to do this.

If you want to replace the default ObjectMapper completely, define a @Bean of that type and mark it as @Primary.

Defining a @Bean of type Jackson2ObjectMapperBuilder will allow you to customize both default ObjectMapper and XmlMapper (used in MappingJackson2HttpMessageConverter and MappingJackson2XmlHttpMessageConverter respectively).


spring.jackson.serialization-inclusion=non_null used to work for us

But when we upgraded spring boot version to 1.4.2.RELEASE or higher, it stopped working.

Now, another property spring.jackson.default-property-inclusion=non_null is doing the magic.

in fact, serialization-inclusion is deprecated. This is what my intellij throws at me.

Deprecated: ObjectMapper.setSerializationInclusion was deprecated in Jackson 2.7

So, start using spring.jackson.default-property-inclusion=non_null instead


You can add a following method inside your bootstrap class which is annotated with @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;
}

I stumbled upon another solution, which is quite nice.

Basically, only do step 2 from the blog posted mentioned, and define a custom ObjectMapper as a Spring @Component. (Things started working when I just removed all the AnnotationMethodHandlerAdapter stuff from step 3.)

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

Works as long as the component is in a package scanned by Spring. (Using @Primary is not mandatory in my case, but why not make things explicit.)

For me there are two benefits compared to the other approach:

  • This is simpler; I can just extend a class from Jackson and don't need to know about highly Spring-specific stuff like Jackson2ObjectMapperBuilder.
  • I want to use the same Jackson configs for deserialising JSON in another part of my app, and this way it's very simple: new CustomObjectMapper() instead of new ObjectMapper().

When I tried to make ObjectMapper primary in spring boot 2.0.6 I got errors So I modified the one that spring boot created for me

Also see 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;
}

I found the solution described above with :

spring.jackson.serialization-inclusion=non_null

To only work starting at the 1.4.0.RELEASE version of spring boot. In all other cases the config is ignored.

I verified this by experimenting with a modification of the spring boot sample "spring-boot-sample-jersey"


I know the question asking for Spring boot, but I believe lot of people looking for how to do this in non Spring boot, like me searching almost whole day.

Above Spring 4, there is no need to configure MappingJacksonHttpMessageConverter if you only intend to configure ObjectMapper.

You just need to do:

public class MyObjectMapper extends ObjectMapper {

    private static final long serialVersionUID = 4219938065516862637L;

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

And in your Spring configuration, create this bean:

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

참고URL : https://stackoverflow.com/questions/28324352/how-to-customise-the-jackson-json-mapper-implicitly-used-by-spring-boot

반응형