Spring multiple entity JSON serializers - spring

I am looking for a way to create multiple json serializers for my entity. I have created service layer, custom serilizers and now I have problem with implementation of this things.
My Service class looks like:
#Service
class TeamsService(#Autowired private val teamsRepository: TeamsRepository) : ITeamsService{
override fun findAll(): String? {
var objectMapper = ObjectMapper()
var simpleModule = SimpleModule()
simpleModule.addSerializer(Teams::class.java, TeamsSerializer())
objectMapper.registerModule(simpleModule)
return objectMapper.writeValueAsString(teamsRepository.findAll())
}
}
And my Controller looks like:
#RestController
#RequestMapping("/v1")
class MainController(#Autowired private val teamsService: TeamsService) {
#GetMapping("/teams")
fun teams(): String? = teamsService.findAll()
}
Now I have problem that my response lost all headers and appears as text/plain not text/json, like it was before I added custom mapper.
I Was reading about projections but I am not sure if I should use them. I do not want to have query parameters in my url.

I found solution. The best way to do this is to use ModelMapper library. You can simply map entity to custom classes and serialize

Related

Providing default values for validation in SpringBoot

I want SpringBoot to be able to provide default values for fields that the user must enter. For example, I have something like this:
*Controller class*
#PostMapping("/test")
public ResponseEntity<> myMethod(#RequestBody #Valid MyContract contract) {}
*MyContract class*
#Valid
DataObject dataObject;
*DataObject class*
#Component
public class DataObject {
private #Value("${field1.default}") String field1Default;
private String field1
public String getField1() {
return (field1 == null ? field1Default : field1);
}
}
The DataObject class needs to be created on a per request basis. There are also other places in the code where it needs to be created on demand. So I imagine it needs to be a Prototype object. But I can't figure out how to get Spring to created it properly when it creates it for the request.
Update
I have read more about #RequstBody, e.g., https://www.javadevjournal.com/spring/spring-request-response-body/ and Should spring #RequestBody class be singleton or prototype?, which explains that the object is not a Component, but a simple POJO that gets the values from the Json request. So it seems that there is no way to inject #Values from the Spring application.properties file. Is there any other way around this? Or another suggested implementation?

Inserting Post Method with Spring Boot

I'm learning Kotlin, part of my project is to integrate JSON as an object and use the POST method to change or add information.
I'm not able to do this, I need help.
package com.example.blog
import org.springframework.web.bind.annotation.*
data class Relatorio(
val titulo: String,
val autor: String,
val serie: String
)
#RestController
#RequestMapping("/Bradesco")
class BradescoController {
#GetMapping()
public fun relatorio(): Relatorio {
val result = Relatorio(
"Investimentos",
"Luis Felipe",
"Bradesco Analises"
)
return result
}
#PostMapping
#RequestMapping( #RequestBody "/empiricus")
public fun relatorio2() {
"titulo" = "Contra as altas taxas"
return "Atualizado";
}
}
It looks like some annotations are out of place in your relatorio2 method. You want to register a REST-endpoint for the POST-method and the path /empiricus.
This can happen one of two ways:
Annotate the method with #RequestMapping(value = "/empiricus", method = RequestMethod.POST)
Annotate the method with `#PostMapping("/empiricus") (you can omit the method-parameter from the example above, since this a shortcut for exactly that.
The #RequestBody annotation needs to be placed in the parameter of the relatorio2 method since it tells Spring to map the POST request-body to an object.
Therefore the method should look something like this:
#PostMapping("/empiricus")
public fun relatorio2(#RequestBody relatorio: Relatorio) {
"titulo" = "Contra as altas taxas"
return "Atualizado";
}
Since you added a path on class level, the complete path to call the method is /Bradesco/empiricus. When the object is available in the relatorio2 method, you can use it in your business logic.

How can I easily cache Kotlin Objects in Redis using json via Jackson?

I have a Spring boot app written in Kotlin where I would like to enable caching in Redis. I'd like to have the objects stored as serialized JSON and ideally don't want to have to register each type that could be potentially cached. I have some configuration that mostly works, with a big caveat.
#Bean
fun redisCacheConfiguration(): RedisCacheConfiguration {
val objectMapper =
ObjectMapper()
.registerModule(KotlinModule())
.registerModule(JavaTimeModule())
.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL, JsonTypeInfo.As.PROPERTY)
val serializer = GenericJackson2JsonRedisSerializer(objectMapper)
return RedisCacheConfiguration
.defaultCacheConfig()
.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(serializer))
}
I'm having a little trouble understanding the different values for DefaultTyping but NON_FINAL seems to be the most expansive. However, since objects in Kotlin are final by default, this only works for objects flagged as "open". Ideally I'd like to avoid having to "open" objects just so they can be cached.
Is there some other way I can make this work?
I had the same problem. You should use "open" classes. But this will not help you with data classes, because you cannot make them "open".
There is a plugin called "all-open" where you can define annotations. If you use these annotations classes become "open", even data classes.
spring-kotlin plugin uses "all-open" plugin under the hood, so spring annotations like #Service, #Component etc. make classes open for AOP, because proxying requires you to inherit from classes.
If you use spring-kotlin plugin, there is nice annotation that makes sense for you problem, it is used in Spring Cache, its name is #Cacheable.
If you use #Cacheable on your classes, they will become open and save their type-info to json (ex: {#class: "com.example.MyClass", ...}) when you include this code:
val objectMapper =
ObjectMapper()
.registerModule(KotlinModule())
.registerModule(JavaTimeModule())
.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL, JsonTypeInfo.As.PROPERTY)
val serializer = GenericJackson2JsonRedisSerializer(objectMapper)
More details: https://kotlinlang.org/docs/reference/compiler-plugins.html
Shortly: You don't have to do anything except adding #Cacheable annotation to the classes you want, and it fits by sense also IMO.
The issues have been solved. Therefore we can remove #Cacheble hack from the code. You have to modify your ObjectMapper with the next implementation
val om = ObjectMapper()
.registerModule(KotlinModule())
.registerModule(JavaTimeModule())
.activateDefaultTyping(BasicPolymorphicTypeValidator.builder()
.allowIfBaseType(Any::class.java)
.build(), ObjectMapper.DefaultTyping.EVERYTHING)
val serializer = GenericJackson2JsonRedisSerializer(om)
Fixed Maven Jackon dependency
<!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-databind -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.10.0.pr2</version>
</dependency>
You can look this:
https://github.com/endink/caching-kotlin
Its support both jackson and kryo
I had a problem since my data classes were extending some interfaces, so generic would not do the trick, I end up with this solution, its a custom serialiser and deserialiser, the generic would just save time compiled getter as a variable and break the deserialise
#Configuration
#EnableCaching
class CachingConfiguration() : CachingConfigurerSupport() {
#Bean
fun configureRedisAction(): ConfigureRedisAction? {
return ConfigureRedisAction.NO_OP
}
#Autowired
private lateinit var redisConnectionFactory: RedisConnectionFactory
companion object {
const val CACHE_KEY = "cache-key"
}
#Bean
override fun cacheManager(): CacheManager? {
return RedisCacheManager.RedisCacheManagerBuilder
.fromConnectionFactory(redisConnectionFactory)
.withCacheConfiguration(CACHE_KEY, cacheConfig<User>(ofMinutes(5)))
.build()
}
private inline fun <reified T> cacheConfig(ttl: Duration): RedisCacheConfiguration {
return RedisCacheConfiguration
.defaultCacheConfig()
.serializeValuesWith(fromSerializer(object : RedisSerializer<Any> {
val mapper = ObjectMapper().registerModule(ParameterNamesModule())
override fun serialize(t: Any?): ByteArray? {
return mapper.writeValueAsBytes(t)
}
override fun deserialize(bytes: ByteArray?): Any? {
return try {
mapper.readValue(bytes!!, T::class.java) as Any
} catch (e: Exception) {
null
}
}
})
)
.entryTtl(ttl)
}
}

RestTemplate getForObject to POJO

I'm using RestTemplate.getForObject() to retrieve json that includes a couple of objects and arrays, I only want to convert one of the objects inside this json to a POJO, I don't care about the other objects inside this json.
What is the proper way to approach this?
Edit:
Another approach from accepted answer, we can use jacksons ObjectMapper
#Autowired
private ObjectMapper jacksonObjectMapper;
then
LinkedHashMap obj1 = restTemplate.getForObject(uri, LinkedHashMap.class, params);
LinkedHashMap obj2 = (LinkedHashMap)test.get("flightStatuses");
Flight flight = jacksonObjectMapper.convertValue(obj2, Flight.class);
You get the idea, just get a generic datatype from your json structure then use ObjectMapper to convert it to the class you need.
One solution would be to create a wrapper class, which includes the POJO you want to deserialize and ignore all other properties using #JsonIgnoreProperties. You would then retrieve the wrapper object and get the POJO from it.
#JsonIgnoreProperties(ignoreUnknown=true)
public class Wrapper {
private MyPojo myPojo;
}
MyPojo myPojo = restTemplate.getForObject("url", Wrapper.class).getMyPojo();

Using Scala classes as DTOs in Spring MVC

In my project I'm using Spring + Scala.
Some of my Spring MVC controllers uses Spring feature for binding incoming HTTP parameters to DTO object. Like this:
#RequestMapping(value = Array("/", ""), method = Array(RequestMethod.POST))
def saveProduct(dto: MyDto): Iterable[MyDto] = {...}
And MyDto is simple scala class:
class MyDto extends Serializable {
#BeanProperty var id : Long = _
#BeanProperty var name: String = _
}
My problem is that I'm getting exceptions when trying to use Scala Option class for fields in MyDto:
class MyDto extends Serializable {
#BeanProperty var id : Option[Long] = None
#BeanProperty var name: Option[String] = None
}
Exception message is:
Failed to convert property value of type 'java.lang.String' to required type 'scala.Option' for property 'name';
What I can do to use Scala Options as type if fields in MyDto?
I am not a Scala expert, but here is one way:
Create a converter, along these lines:
import org.springframework.core.convert.converter.Converter
class TypeToOptionOfTypeConverter[T] extends Converter[T, Option[T]] {
override def convert(source: T): Option[T] = {
Some(source)
}
}
Register this converter with Spring MVC:
class WebConfig extends WebMvcConfigurerAdapter {
override def addFormatters(registry: FormatterRegistry): Unit = {
registry.addConverter(classOf[String], classOf[Option[String]], new TypeToOptionOfTypeConverter[String])
registry.addConverter(classOf[Long], classOf[Option[Long]], new TypeToOptionOfTypeConverter[Long])
}
}
That should be it, now your DTO should get cleanly mapped.
Spring has support for converting types using converters with its data binding. You will need to implement the converter so that Spring knows how to convert, for example, String to Option[String].
See:
http://docs.spring.io/spring/docs/current/spring-framework-reference/html/validation.html#core-convert

Resources