Adding custom Jackson Module to Spring boot's ObjectMapper is ignored - spring-boot

I use SpringBoot v2.2.2RELEASE (very old version) and I configured a custom Module as follow:
#Configuration
public class JacksonConfig {
#Bean
public Module jacksonModule() {
SimpleModule module = new SimpleModule();
module.addSerializer(Object.class, new MyCustomSerializer());
return module;
}
}
This works well and it does what it supposed to do.
Recently, I've upgraded to the lates SpringBoot 2.7.0 and the serializer gets ignored! I can see that when my app loads then it instantiates a new Module instance, but it doesn't call the serializer anymore...
(What the serializer does is add a root node to the response (json) that returns to the client via REST Controller).
Any idea?
EDIT1
I tried to debug it by put a breakpoint in JacksonAuthConfiguration#configureModules and I saw the module in the list as well as some other Swagger related modules.

So if I understand it correct your problem is that Spring doesnt take this ObjectMapper for the RestController. And in this case I struggled with this problem aswell. I tried everything from defining a primary bean for ObjectMapper, defining a bean for the factor adding the module as you did but nothing worked.
So in the end my solution was to use #JsonDeserialize and #JsonSerialize annotations.

Related

MappingJackson2HttpMessageConverter isn't using the ObjectMapper configured by a #Bean

i am using spring-boot 3.0.2 and have created a custom jackson serializer and registered it using #JsonComponent. i have confirmed it is registered in the ObjectMapper instance that gets injected using #Autowire. when i go to invoke the rest endpoint that will return an object that should be handled by the custom serializer, it is never by the RequestResponseBodyMethodProcessor.
after doing some digging it seems only the default set of converters created in WebMvcConfigurationSupport are being used and nothing else. i have tried registering my own instance of MappingJackson2HttpMessageConverter as a #Bean but it is never used.
the only way i have found to fix this issue is to replace whatever default is being created using extendMessageConverters in WebMvcConfigurer.
can anyone offer any suggestions? from the various blog posts i've read, this should just work but appears not to.
i expect json that looks like this:
["fqdn":"test.com"]
but instead get:
["fqdn:{"idnEncoded":false,"domainName":"test","tldName":"com"}]

SpringMVC global setting for ignoring unknown properties during deserialization

Spring Boot sets "spring.jackson.deserialization.fail-on-unknown-properties=false" by default. I have a library that works fine in Spring Boot, but when used in an existing SpringMVC app it throws "Unrecognized field, not marked as ignorable". Is there some comparable global setting for SpringMVC I can set in the config or otherwise?
edit: spring webmvc version 3.2.15.RELEASE
You can annotate the mapped classes with
#JsonIgnoreProperties(ignoreUnknown = true)
or create add the following configuration to the ObjectMapper as follows:
objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
You can follow two method that I have mention in this answer. If I'm not wrong either one will work for you. (But method 1 won't work if your clinet class does not have a no-arg default constructor)

Split jackson configuration into separate properties

I'm using Spring Boot 2.2.5.RELEASE and would like to split my application.properties into separate files. There are already similar questions on StackOverflow but none of them seem to work for configuring Jackson.
My current non working solution is the following:
root/
- application.properties (without Jackson configuration)
- jackson-configuration.properties (includes Jackson configuration)
Jackson configuration class:
#Configuration
#PropertySource("/jackson-configuration.properties")
public class JacksonConfiguration {
}
Please note, I've tried different ways to specify the path including:
"/jackson-configuration.properties"
"jackson-configuration.properties"
"classpath:/jackson-configuration.properties"
"classpath:jackson-configuration.properties"
Spring Boot does not seem to use the configuration. If I copy it over into the application.properties - it works.
Content of jackson-configuration.properties:
spring.jackson.property-naming-strategy=SNAKE_CASE
spring.jackson.mapper.sort-properties-alphabetically=true
spring.jackson.deserialization.fail-on-unknown-properties=true
spring.jackson.parser.strict-duplicate-detection=true
spring.jackson.time-zone=Europe/Zurich
My application is annotated with #SpringBootApplication , so it should scan for additional properties.
/edit
I just realized the problem is the testing, not the productive code itself. If I start the application it works. What doess not work is testing with #JsonTest. I can fix this problem by adding the following line to my tests #ContextConfiguration(classes = {JacksonConfiguration.class}). But in turn, this causes the annotation #JsonComponent to stop working but only for the #JsonTest annotated classes.
See the documentation here. Here is an excerpt from the documentation
In order to resolve ${...} placeholders in definitions or
#Value annotations using properties from a PropertySource, you must
ensure that an appropriate embedded value resolver is registered in
the BeanFactory used by the ApplicationContext. This happens
automatically when using in XML. When
using #Configuration classes this can be achieved by explicitly
registering a PropertySourcesPlaceholderConfigurer via a static #Bean
method.
You need to create a bean like this
#Bean
public static PropertySourcesPlaceholderConfigurer devPropertyPlaceholderConfigurer() throws IOException {
PropertySourcesPlaceholderConfigurer configurer = new PropertySourcesPlaceholderConfigurer();
configurer.setLocations(new PathMatchingResourcePatternResolver().getResources("file:pathtToFile"));
configurer.setIgnoreUnresolvablePlaceholders(true);
return configurer;
}

How can I force #AutoConfigureJsonTesters to use HAL format from spring-hateoas?

I have a small module that should only contain the resource model of my REST service. I want to create a test in this module to ensure that the resource model serializes and deserializes appropriate HAL format.
I have a single test and this is the configuration:
#RunWith(SpringRunner::class
#SpringBootTest
#AutoConfigureJsonTesters
class MyTest {
#Autowired
private lateinit var collectionTester: JacksonTester<Resources<Entity>>
....
}
and a very simple configuration
#SpringBootConfiguration
class TestConfig
When calling collectionTester.write on a list of Entity (which extends ResourceSupport) I don't get an _embedded field, instead I get
{"links":[],"content":[...]}
which is not HAL format.
How can I force #AutoConfigureJsonTesters to give me a JacksonTester with an ObjectMapper configured for HAL?
Spring Boot 2.0.0.RELEASE
Thanks!
The auto-configured JacksonTester will use the context’s ObjectMapper which won’t have any of the Spring HATEOAS stuff configured on it. You might be better creating a JacksonTester yourself and passing it an appropriately configured ObjectMapper to use.
I believe Spring HATEOAS has a module that it applies to the ObjectMapper to configure it. If you get stuck with that, asking in gitter/spring-projects/spring-data is probably your best bet as Spring HATEOAS is maintained by the Data team due to it being used by Spring Data REST.

Resolving bean conflicts in third-party packages

I have an application, let's call it MyApp. An example of the build.gradle looks like this:
compile group: 'com.example', name: 'library'
compile .. spring-heatoas ..
Now, the way this library works is that it expects the host to give it a bean of type ObjectMapper. It by itself does not define an ObjectMapper as the configuration of the same is completely open to the host library. So basically, in the host MyApp I have this config:
#Configuration
public class SpringConfig {
#Bean
public ObjectMapper objectMapper() { ... }
}
Everything is fine and working, till I had the spring-hateos dependency. spring-hateos defines it's own ObjectMapper which goes by the name _hal_objectMapper. So, after adding it, I get a conflict between the two beans. I tried:
#Bean
#Order(Ordered.HIGHEST_PRECEDENCE)
public ObjectMapper objectMapper() { ... }
Not only does this not work, but even if it did, I guess it has the potential to break functionality as the host application can no longer configure the ObjectMapper. A straightforward solution obviously is to make the library accept a named-bean and then declare it in my host application with a #Qualifier annotation. But getting the library to change (and there are a couple of libraries like that) company-wide is going to be a major pain. While we are considering that solution, is there a way to solve this?
The annotation #Primary advises spring to use that annotated bean if many beans of the same type are available. This could resolve your issue.
Your code should look like this :
#Configuration
public class SpringConfig {
#Bean
#Primary
public ObjectMapper objectMapper() { ... }
}
The problem may be now, that spring-hateos also uses this objectMapper, which is not configured as expected.
A complete solution could be, to create a child spring context.
Only for your ObjectMapper and the 3rd party bean, so that the objectMapper is not visible for the rest of you application.
How this is done depends on how you instantiate the 3rd party bean, and also where it will be used. With the provided information I cannot describe that more detailed.
You can find a good entry here :
modularizing-configurations
(see chapter 'Nesting #Configuration classes' or search for 'child')

Resources