Jackson JsonParseExceptionMapper and JsonMappingExceptionMapper shadow custom mapper

My project uses Spring Boot + Jersey 2.

I created a custom Jackson mapper for JsonParseException, but it did not receive the call, instead, the standard Jackson JsonParseExceptionMapper was used.

My custom mapper:

package com.rmn.gfc.common.providers; import ... @Provider public class JsonParseExceptionHandler implements ExceptionMapper<JsonParseException> { @Override public Response toResponse(JsonParseException exception) { return Response .status(Response.Status.BAD_REQUEST) // entity ... .build(); } } 

I register my cartographer as follows:

 @Component public class OrderServiceResourceConfig extends ResourceConfig { public OrderServiceResourceConfig() { packages("com.rmn.gfc.common.providers"); } } 

I'm sure the mapper registration is fine because the other custom mappers from this package work, but one for JsonParseException is hidden by Jersey's standard JsonParseExceptionMapper.

How can I override the standard Jackson JsonParseExceptionMapper with my implementation?

+1
java spring-boot jersey
source share
2 answers

I have found a solution that suits me.
Use javax.annotation.Priority on your custom cartographer to override Jackson's default cartography, for example:

 @Provider @Priority(1) public class JsonParseExceptionHandler implements ExceptionMapper<JsonParseException> { // ... } 

OR, if you register JAX-RS components through ResourceConfig, you can specify the priority as follows:

 public class MyResourceConfig extends ResourceConfig { public MyResourceConfig() { register(JsonMappingExceptionHandler.class, 1); register(JsonParseExceptionHandler.class, 1); // ... } } 

The lower number is the highest priority.
javax.ws.rs.Priorities has certain predefined constants for priorities.

+3
source share

You can blame JacksonFeature

JacksonFeature registers the default exception JacksonFeature for Jackson exceptions if JacksonJaxbJsonProvider not registered. And that’s exactly what you don’t want.

See the relevant parts of the source code :

 // Register Jackson. if (!config.isRegistered(JacksonJaxbJsonProvider.class)) { // add the default Jackson exception mappers context.register(JsonParseExceptionMapper.class); context.register(JsonMappingExceptionMapper.class); ... } 

Spring Boot registers JacksonFeature

The Spring Boot JerseyAutoConfiguration class JerseyAutoConfiguration register JacksonFeature if it is in the classpath. See the relevant parts of the source code :

 @ConditionalOnClass(JacksonFeature.class) @ConditionalOnSingleCandidate(ObjectMapper.class) @Configuration static class JacksonResourceConfigCustomizer { ... @Bean public ResourceConfigCustomizer resourceConfigCustomizer( final ObjectMapper objectMapper) { addJaxbAnnotationIntrospectorIfPresent(objectMapper); return (ResourceConfig config) -> { config.register(JacksonFeature.class); config.register(new ObjectMapperContextResolver(objectMapper), ContextResolver.class); }; } ... } 

Bypass

As a workaround, you can register JacksonJaxbJsonProvider and then register your custom display mechanisms (or just annotate them with @Provider to automatically detect Jersey):

 @Component public class OrderServiceResourceConfig extends ResourceConfig { public OrderServiceResourceConfig() { packages("com.rmn.gfc.common.providers"); register(JacksonJaxbJsonProvider.class); // Register other providers } } 

See what JacksonJaxbJsonProvider documentation says:

The JSON content type provider is automatically configured to use Jackson and JAXB annotations (in this order of priority). Otherwise, functionally the same as JacksonJsonProvider .

Alternative solution

Alternatively, you can get rid of the jersey-media-json-jackson and use jackson-jaxrs-json-provider . With this, you get rid of JacksonFeature , and then you can register your own display engines.

It was mentioned in this.

What seems like the right decision

As stated in the Kysil Ivan answer , write your own exception display unit and give it a high priority, e.g. 1 . If you use automatic detection, just comment it with @Provider and @Priority :

 @Provider @Priority(1) public class JsonParseExceptionMapper implements ExceptionMapper<JsonParseException> { ... } 

If you manually register your provider, you can give the provider a binding priority :

 @ApplicationPath("/") public class MyResourceConfig extends ResourceConfig { public MyResourceConfig() { register(JsonParseExceptionMapper.class, 1); } } 
+2
source share

All Articles