-
Notifications
You must be signed in to change notification settings - Fork 472
@EnableHypermediaSupport is not compatible with Spring Boot's Jackson2ObjectMapperBuilder #333
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Comments
Are you using HAL? If so, we found that Spring HATEOAS constructs its own object mapper. In order to address this in our project, we did the following to get the whole application using the same configuration.
|
I believe this is because Spring HATEOAS uses its own object mapper. You will have to set these flags on that instance. You can access the object mapper using the bean factory: private static final String HAL_OBJECT_MAPPER_BEAN_NAME = "_halObjectMapper";
@Autowired
private BeanFactory beanFactory
...
@Bean
public ObjectMapper objectMapper() {
ObjectMapper halObjectMapper = beanFactory.getBean(HAL_OBJECT_MAPPER_BEAN_NAME);
//set your flags
return halObjectMapper
} |
Thanks for the workaround, that works. However, Spring HATEOAS should really honor any settings configured via Similarly, the HATEOAS ObjectMapper ignores |
i think that is a side-effect of Spring HATEOAS having its own module and mapper to handle HAL. I ended up configuring the HAL mapper to recognize |
Yes we use a custom |
If you would like to have ISO dates with Jackson you can use the following annotation:
|
From what I have understood so far, most ObjectMapper are defined in the RepositoryRestMvcConfiguration. There is even a "halObjectMapper" which would get all the adjustments from the RepositoryRestConfigurerAdapter. HATEOAS uses a bean named "_halObjectMapper" which is not using the RepositoryRestConfigurerAdapter settings :-) |
RepositoryRestMvcConfiguration and RepositoryRestConfigurerAdapter are parts of Spring Data REST and not available in projects confined to Spring HATEOAS. |
You are right. Its more the other way round (Spring Data REST is using HATEOAS). So this make no sense.
|
We had a related issue reported on Spring Framework side (SPR-13608). I would like to investigate and potentially propose a fix that avoid to have duplicated Not sure yet if the fix will be on Spring HATEOAS or Spring Boot side, buy I am going to investigate and send my feedback here. |
I had a look to what is related to class HypermediaSupportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
private static final String HAL_OBJECT_MAPPER_BEAN_NAME = "_halObjectMapper";
@Override
public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
if (types.contains(HypermediaType.HAL)) {
if (JACKSON2_PRESENT) {
BeanDefinitionBuilder halQueryMapperBuilder = rootBeanDefinition(ObjectMapper.class);
registerSourcedBeanDefinition(halQueryMapperBuilder, metadata, registry, HAL_OBJECT_MAPPER_BEAN_NAME);
BeanDefinitionBuilder customizerBeanDefinition = rootBeanDefinition(DefaultObjectMapperCustomizer.class);
registerSourcedBeanDefinition(customizerBeanDefinition, metadata, registry);
BeanDefinitionBuilder builder = rootBeanDefinition(Jackson2ModuleRegisteringBeanPostProcessor.class);
registerSourcedBeanDefinition(builder, metadata, registry);
}
}
private List<HttpMessageConverter<?>> potentiallyRegisterModule(List<HttpMessageConverter<?>> converters) {
for (HttpMessageConverter<?> converter : converters) {
if (converter instanceof MappingJackson2HttpMessageConverter) {
MappingJackson2HttpMessageConverter halConverterCandidate = (MappingJackson2HttpMessageConverter) converter;
ObjectMapper objectMapper = halConverterCandidate.getObjectMapper();
if (Jackson2HalModule.isAlreadyRegisteredIn(objectMapper)) {
return converters;
}
}
}
ObjectMapper halObjectMapper = beanFactory.getBean(HAL_OBJECT_MAPPER_BEAN_NAME, ObjectMapper.class);
MappingJackson2HttpMessageConverter halConverter = new TypeConstrainedMappingJackson2HttpMessageConverter(ResourceSupport.class);
halConverter.setObjectMapper(halObjectMapper);
List<HttpMessageConverter<?>> result = new ArrayList<HttpMessageConverter<?>>(converters.size());
result.add(halConverter);
result.addAll(converters);
return result;
}
} On Spring Boot side, it detects So what about making Spring Boot registering an Based on my Any thoughts @olivergierke @gregturn ? |
Has there been any progress on this issue? We've experienced this issue in other places in our application since I reported SPR-13608 and it's really making things difficult for us. |
hi, I'm facing almost the "same" problem. I don't use Spring Data Rest but only Spring Boot 1.3.0, Spring MVC and Spring Hateoas and i have my own So i must do a workaround
Do you have any plan for this issue? !!! Updated !!! |
I am not able to use use Spring Boot in my environment. Only Spring HATEOAS and Spring Data REST. The only way I was able to globally format my dates was to follow the third work around provided by @thomasletsch . |
I'm also loosing the Java 8 |
If it's any help, I ended up mixing @gpaul-idexx and @thomasletsch solutions
This might be a more Spring-Boot approach to rendering Java8 dates with the HAL Jackson Object Mapper. |
The solution described by @thebignet doesn't work for me, because the Spring HATEOAS There seems to be an implementation attempt already at Spring Boot, which however does not work: [https://github.com/spring-projects/spring-boot/blob/v1.3.2.RELEASE/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/hateoas/HypermediaAutoConfiguration.java] contains a class |
Actually, I didn't use a postProcessor, since I can't really trust order. Instead, I used an @Autowired dependency, which means that _halObjectMapper bean has to be created before my objectMapper bean. This objectMapper is the same as _halObjectMapper, but enhances it during its creation. @aristotelos, try to use a dependant @bean declaration instead of using a PostProcessor (see my example) and see if that works for you. |
@thebignet You're right, I tested your solution and it works! The fault was on my side. At my first try of your solution it didn't seem to work as well. My use case was enabling the |
I have this problem too... Jackson annotations like I find it interesting that if This issue is marked as waiting-for-feedback. From who? |
I had the same issues, spent quite some time to figure out why "spring.jackson.serialization.indent_output=true" in my application.properties was not being applied. Turned out that turning on/off the @EnableHypermediaSupport annotation made the difference. I'm also hoping this can be fixed, because it's quite confusing, hard to google, and hard to debug. |
To expand on @thebignet's solution you can apply the default configuration from Boot's Jackson2ObjectMapperBuilder by injecting it and using its configure method.
UpdateSection 27.1.8 - Spring HATEOAS in the Spring Boot docs indicates that simply leaving off the @EnableHypermediaSupport annotation will let Boot's autoconfiguration kick in for HATEOAS and create a proper ObjectMapper with the standard modules loaded and properties and features applied. |
Can't upgrade from Boot 1.2 to 1.3 because of this. Is the issue gonna be fixed? |
If you're trying to get your hands on the ObjectMapper used for HAL, and already asking the app context for that bean, this is a crude option: Be advised we're aware of this issue and plan to tackle it in the near future, after wrapping up work on the Affordance API (https://github.com/spring-projects/spring-hateoas/tree/rebase/affordances). |
@gregturn Yes, see in my snippet I am attempting to get a handle on the |
I can't trigger the default spring boot autoconfiguration:
I am not using the Edit: It's combing from the RepositoryRestMvcConfiguration in spring data rest. |
Arrrgghhh, this cost me a few precious hours. I could not find why I wasn't able to get the LocalDateTime to be "jsonized" as timestamps... FYI: As workaround until this is fixed: Just add this class or just the bean definitions to your project, then the default spring boot registration for the object mapper is used again. Even with HAL
Is there a reason why there is a second objectmapper? |
Hello, I'm using SpringBoot 1.5.3.RELEASE and spring-hateoas 0.23 I have the same issue and the work around from @pcornelissen here my config class :
any ideas why ? |
I've sifted through all the comments, and have an example github repo (Spring Boot 1.5.4 + Spring HATEOS 0.23) that covers this in detail (https://github.com/gregturn/spring-hateoas-customize-jackson). If you read the Spring Boot docs, you have multiple options:
Both of these solutions appear simpler to plug in your custom It's also possible to create your own
Notice the last sentence in that quote: "You will disable all autoconfiguration of the ObjectMapper". That's why this is NOT recommended. I haven't reconciled this with Spring Data REST's HAL-based object mapper (yet). But I wanted to clarify the proper approach. P.S. This probably would make good material for Spring HATEOAS's reference docs. |
To take the comment from @gregturn a bit further, I agree that the best solution is to work off of Spring Boot's auto-configuration instead of creating your own custom Here is an example of a very simple solution which worked for me (using Spring Boot 1.5.2 and Spring HATEOAS 0.23.0, and it is also using Spring Boot's own API for customizing the auto-configured
It's clean and simple, and not very invasive either. |
This is definitely a problem as of Spring Boot 2 M4 as well. My API requires null values to not be serialized but I also needed to use Spring Boot 2 to support Spring Data Elasticsearch with ES 5.X. After isolating the issue to being a Hateoas related, the following configuration class was able to sort out my problems: @Configuration
public class JacksonConfiguration {
private static final String HAL_OBJECT_MAPPER_BEAN_NAME = "_halObjectMapper";
private final BeanFactory beanFactory;
@Autowired
public JacksonConfiguration(BeanFactory beanFactory) {
this.beanFactory = beanFactory;
}
@Bean
public ObjectMapper objectMapper() {
ObjectMapper mapper = (ObjectMapper) beanFactory.getBean(HAL_OBJECT_MAPPER_BEAN_NAME);
mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
return mapper;
}
} |
I had a similar issue with spring boot and HATEOAS. The workaround was to register a bean post processor, rather than creating multiple beans of the same type.
|
I just ran into this problem, I followed a similar solution to @ashaffer1903 but with an autowired
|
Would be nice to have this one documented as a "gotcha" in the docs. |
We've just pushed some significant configuration changes to Spring HATEOAS (both on master as well on the bugfix branch for the upcoming 0.25.0) in the context of #719 #723. I've adapted Spring Data REST to work with these changes (coming in Lovelace) and verified our Spring Data examples work with these changes on Boot 2.0.3. Find the details of what has changed in the tickets I linked above. I'd like to encourage everyone who brought up issues in this ticket to try to upgrade to Spring HATEOAS 0.25.0.BUILD-SNAPSHOT (it doesn't contain any major changes but the ones just described), give it a spin and report problems you (still see) in #719. |
I just updated to Spring Boot 2.0.4 and Spring HATEOAS 0.25.0 which lead to a failing Jackson configuration as _halObjectMapper is no longer available. I was trying to refactor my code according to the changes in #719, but to no avail. With 0.25.0, what would be a simple way to configure the ObjectMapper that is being used by Spring HATEOAS? |
As described in the commit that introduces the change, Spring HATEOAS will now reuse an |
Hello, Is there any clear instruction on how to add a custom media type ( |
This issue was about customizing a Jackson ObjectMapper (presumably to alter indentation etc) not registering another media type. We haven’t hammered out the APIs to simplify the process of creating your own media type But you’re free to look up how Spring HATEOAS registers a custom message converter for each custom Jackson module and attempt the same. PS PRs welcome! |
@gregturn I successfully did this with previous versions of Spring HATEOS by getting the bean named _halObjectMapper. When changing this to jacksonObjectMapper I have no success |
Spring HATEOAS picks up the primary |
Here is my configuration
Putting a break point after the super.addDefaultHttp... shows me 9 converters and the one I created above is present and I have and still I am getting this response
Any help much appreciated |
After #728 is reviewed and merged to master, I have something in progress that will make it much simpler to register your own media types. |
See #833 for custom media types. |
I am trying to customize Jackson serialization for ISO dates. Per Spring Boot instructions, I created a
@Bean
of typeJackson2ObjectMapperBuilder
:However, I find that these settings are not applied when using
@EnableHypermediaSupport
. When I remove the annotation, I see the effects of the Jackson serialization settings.The text was updated successfully, but these errors were encountered: