How do you configure kotlinx serialization in Spring Boot? - spring-boot

I am wondering how I can configure kotlinx as a default serialization in Spring Boot app.
For Jackson, I would use e.g. spring.jackson.deserialization.fail-on-unknown-properties=false
But I haven't found any configuration options for kotlinx in Spring.
I know kotlinx supports this, I only cannot find a way to configure it on Spring level so that it works for example in controller method signatures:
#PostMapping("/foo")
fun fooMethod(
#RequestBody fooJsonRequest: SomeDataClassRepresentingTheJson,
) {}
↑ Throws HttpMessageNotReadableException exception suggesting I use ignoreUnknownKeys=true when constructing kotlinx.serialization.json.Json. However I do not know where to do this so it would apply to Spring itself.
I have tried creating a bean providing Json object to no avail.

Publish a #Bean of the following type and customize Json constructor as needed:
#Bean
fun messageConverter(): KotlinSerializationJsonHttpMessageConverter {
return KotlinSerializationJsonHttpMessageConverter(Json {
ignoreUnknownKeys = true
})
}

Related

Spring Boot 2.1.4: #Autowired does not work in custom Jackson Serializers/Deserializers, how to enable it?

I am struggeling to get a Spring Component #Autowired into my custom Deserializer.
Example:
#JsonDeserialize (using = SomeClassJsonDeserializer.class)
SomeClass {
[...]
}
#JsonComponent
SomeClassJsonDeserializer extends JsonDeserializer<SomeClass> {
#Autowired
private SomeService service;
#Override
public SomeClass deserialize(JsonParser jsonParser,
DeserializationContext deserializationContext) throws IOException, JsonProcessingException {
[...]
// this.service is null
}
}
I found mainly two possible solutions which didn't work for me at all:
use SpringBeanAutowiringSupport in default constructor of Deserializer
use HandlerInstantiator (via config / custom implementation)
I am using only those Jackson annotations shown in the example above to 'configure' the Jackson parsing.
There is no additional custom configuration affecting Jackson in any way besides the default SpringBoot auto configuration. When using #EnableWebMvc (which breaks Spring-Boot auto configuration so I don't want to use it), the Component-wiring does work as expected.
Is there any official / recommended solution for plain Spring-Boot with default auto configuration ?
The problem was with how I used Spring's RestTemplate.
For a remote call, I created a new Instance of RestTemplate by contructor call (new RestTemplate()).
This way, Spring wasn't able to configure the RestTemplate - bean correctly (so that SpringBoot autoconfigure and Jackson autoconfigure 'connect' together, resulting in working Spring-DI in custom Jackson components).
I simply had to #Autowire the RestTemplateBuilder bean instance provided by Spring, and then call RestTemplateBuilder.build() to aqquire a RestTemplate bean instance created by Spring.

How to Inject custom method argument in Spring WebFlux using HandlerMethodArgumentResolver?

I want to create an custom method argument Resolver using Spring WebFlux. I am following link but its seem to be not working.
I am able to create the custom argument resolver using WebMvc.
import org.springframework.web.reactive.result.method.HandlerMethodArgumentResolver;
public class MyContextArgumentResolver implements HandlerMethodArgumentResolver {
#Override
public boolean supportsParameter(MethodParameter parameter) {
return MyCustomeObject.class.isAssignableFrom(parameter.getParameterType())
}
#Override
public Mono<Object> resolveArgument(MethodParameter parameter, BindingContext bindingContext,
ServerWebExchange exchange) {
.....
return Mono.just(new MyCustomeObject())
}
Please note that i am using HandlerMethodArgumentResolver from .web.reactive. package.
My AutoConfiguration file look like
#Configuration
#ConditionalOnClass(EnableWebFlux.class) // checks that WebFlux is on the class-path
#ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.REACTIVE)//checks that the app is a reactive web-app
public class RandomWebFluxConfig implements WebFluxConfigurer {
#Override
public void configureArgumentResolvers(ArgumentResolverConfigurer configurer) {
MyContextArgumentResolver[] myContextArgumentResolverArray = {contextArgumentResolver()};
configurer.addCustomResolver(myContextArgumentResolverArray );
}
#Bean
public MyContextArgumentResolver contextArgumentResolver() {
return new MyContextArgumentResolver ();
}
My spring.factories looks like
# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.XXXX.XXX.XXX.RandomWebFluxConfig
Please note that above configuration is part of the jar which is added in Spring WebFlux Boot project enabled using #EnableWebFlux .
It seems you're conflating two different problems here.
First, you should make sure that your method argument resolver works in a regular project.
For that, you need a #Configuration class that implements the relevant method in WebFluxConfigurer. Your code snippet is doing that but with two flaws:
Your configuration is using #EnableWebFlux, which is disabling the WebFlux auto-configuration in Spring Boot. You should remove that
it seems you're trying to cast a list of MethodArgumentResolver into a single instance and that's probably why things aren't working here. I believe your code snippet could be just:
configurer.addCustomResolver(contextArgumentResolver());
Now the second part of this question is about setting this up as a Spring Boot auto-configuration. I guess that you'd like WebFlux applications to automatically get that custom argument resolvers if they depend on your library.
If you want to achieve that, you should first make sure to read up a bit about auto-configurations in the reference documentation. After that, you'll realize that your configuration class is not really an auto-configuration since it will be applied in all cases.
You should probably add a few conditions on that configuration like:
#ConditionalOnClass(EnableWebFlux.class) // checks that WebFlux is on the classpath
#ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.REACTIVE) // checks that the app is a reactive web app

How to make globally shared objects available to freemarker templates in Spring Boot 2

What is the best way of making global shared objects available to freemarker templates when using Spring Boot 2.x, without losing Spring Boot's FreeMarker auto configuration?
The underlying mechanism for doing this is Spring Boot's FreeMakerConfigurer.setFreemarkerVariables, which in turn calls FreeMarker's Configuration.setAllSharedVariables
However, there is no obvious way (to me) to modify the FreeMarkerConfigurer that is setup by FreeMarkerServletWebConfiguration beyond the predefined freemarker properties that Spring Boot supports. (Search for "freemarker" here).
A common approach is to create a custom FreemarkerConfigurer bean, but I believe that then loses some of the auto configuration provided by spring boot, especially around the handling of various external properties.
One option that seems to work is to use a BeanPostProcessor like this:
public class CustomFreeMarkerConfig implements BeanPostProcessor {
Object sharedWithAllFreeMarkerTemplatesObj = new Object();
#Override
public Object postProcessBeforeInitialization(Object bean, String beanName)
throws BeansException {
if (bean instanceof FreeMarkerConfigurer) {
FreeMarkerConfigurer configurer = (FreeMarkerConfigurer) bean;
Map<String, Object> sharedVariables = new HashMap<>();
sharedVariables.put("obj", sharedWithAllFreeMarkerTemplatesObj);
configurer.setFreemarkerVariables(sharedVariables);
}
return bean;
}
}
It seems like there should be a cleaner way of doing it, perhaps by somehow extending or configuring FreeMarkerConfigurationFactory, but I haven't been able to find it.
I found a solution from spring git
Spring Boot 2.0 breaks the solution provided by #wo8335224, as FreeMarkerWebConfiguration is replaced by FreeMarkerServletWebConfiguration, which is unfortunately package-private and thus cannot be subclassed.
A currently working solution is to configure freemarker.template.Configuration bean:
#Configuration
public class FreemarkerConfig {
public FreemarkerConfig(freemarker.template.Configuration configuration) throws TemplateModelException {
configuration.setSharedVariable("name", "whatever type of value");
}
}
Internally FreeMarkerConfigurer#setFreemarkerVariables delegates its work to freemarker.template.Configuration#setAllSharedVariables.

Could not write JSON: No serializer found for class net.i2p.crypto.eddsa.math.ed25519.Ed25519LittleEndianEncoding

i try to implement spring web server to a cordapp , but continuously getting same Serialization error.
{
"timestamp": 1529999846743,
"status": 500,
"error": "Internal Server Error",
"exception": "org.springframework.http.converter.HttpMessageNotWritableException",
"message": "org.springframework.http.converter.HttpMessageNotWritableException: Could not write JSON: No serializer found for class net.i2p.crypto.eddsa.math.ed25519.Ed25519LittleEndianEncoding and no properties discovered to create BeanSerializer (to avoid exception, disable SerializationFeature.FAIL_ON_EMPTY_BEANS); nested exception is com.fasterxml.jackson.databind.exc.InvalidDefinitionException: No serializer found for class net.i2p.crypto.eddsa.math.ed25519.Ed25519LittleEndianEncoding and no properties discovered to create BeanSerializer (to avoid exception, disable SerializationFeature.FAIL_ON_EMPTY_BEANS) (through reference chain: java.util.ArrayList[0]->java.util.Collections$SingletonMap[\"state\"]->java.util.Collections$SingletonMap[\"data\"]->java.util.LinkedHashMap[\"exitKeys\"]->java.util.LinkedHashSet[0]->net.i2p.crypto.eddsa.EdDSAPublicKey[\"params\"]->net.i2p.crypto.eddsa.spec.EdDSANamedCurveSpec[\"curve\"]->net.i2p.crypto.eddsa.math.Curve[\"field\"]->net.i2p.crypto.eddsa.math.Field[\"encoding\"])",
"path": "/api/obligation/cash"
}
create an ObjectMapper in a #Configuration class like this :
#Configuration
class Plugin {
#Bean
fun registerModule(): ObjectMapper {
return JacksonSupport.createNonRpcMapper()
}
}
and you are good to go !
Basically you're trying to serialize an object of class net.i2p.crypto.eddsa.math.ed25519.Ed25519LittleEndianEncoding and as you can see in the sources this class has no fields, which causes the serialization to fail.
You should add this line to your mapper configuration:
mapper.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false);
then see if the output is what you want.
Edit: According to Spring Boot documentation, you can add this configuration to the default mapper by adding
spring.jackson.serialization.FAIL_ON_EMPTY_BEANS=false
to your application.properties file.
Please use this at class level for the bean:
#JsonIgnoreProperties(value={"hibernateLazyInitializer","handler"})
This error throws when Jackson tries to serialize the PublicKey class to JSON format.
Corda provides Jackson support for some intrinsic classes and you have to register that module in Spring:
Add corda-jackson dependency in your spring project.
net.corda:corda-jackson:3.1-corda
Register the Corda Jackson Support Module for your spring project. You can do it using java config as below:
#Bean
public Module registerModule() {
return JacksonSupport.INSTANCE.getCordaModule();
}
This should be because of lacking the JacksonSupport or improper configuration for corda-jackson.
In my case:
I already had:
implementation("com.fasterxml.jackson.module:jackson-module-kotlin")
But the error was still there.
I added corda-jackson in build.gradle.kts.
implementation( "net.corda:corda-jackson:4.3")
I added the following Bean JacksonSupport to the #SpringBootApplication file.
#Bean
#Primary
fun objectMapper(builder: Jackson2ObjectMapperBuilder): ObjectMapper? {
return JacksonSupport.createNonRpcMapper().findAndRegisterModules()
}
And still the error was there.
Then, I added following line to the build.gradle.kts
extra["jackson.version"] = "2.10.2"
And it worked finally :)

How to use "Functional bean definition Kotlin DSL" with Spring Boot and Spring WebFlux?

At https://github.com/spring-projects/spring-framework/blob/master/spring-context/src/main/kotlin/org/springframework/context/support/BeanDefinitionDsl.kt the comment shows how to define Spring Beans via the new "Functional bean definition Kotlin DSL". I also found https://github.com/sdeleuze/spring-kotlin-functional. However, this example uses just plain Spring and not Spring Boot. Any hint how to use the DSL together with Spring Boot is appreciated.
Spring Boot is based on Java Config, but should allow experimental support of user-defined functional bean declaration DSL via ApplicationContextInitializer support as described here.
In practice, you should be able to declare your beans for example in a Beans.kt file containing a beans() function.
fun beans() = beans {
// Define your bean with Kotlin DSL here
}
Then in order to make it taken in account by Boot when running main() and tests, create an ApplicationContextInitializer class as following:
class BeansInitializer : ApplicationContextInitializer<GenericApplicationContext> {
override fun initialize(context: GenericApplicationContext) =
beans().initialize(context)
}
And ultimately, declare this initializer in your application.properties file:
context.initializer.classes=com.example.BeansInitializer
You will find a full example here and can also follow this issue about dedicated Spring Boot support for functional bean registration.
Another way to do it in Spring Boot would be :
fun main(args: Array<String>) {
runApplication<DemoApplication>(*args) {
addInitializers(
beans {
// Define your bean with Kotlin DSL here
}
)
}
}
You can define your beans in *Config.kt file and implement initalize method of ApplicationContextInitializer interface.
override fun initialize(applicationContext: GenericApplicationContext) {
....
}
Some bean definition here.
bean<XServiceImpl>("xService")
bean("beanName") {
BeanConstructor(ref("refBeanName"))
}

Resources