- Saved searches
- Use saved searches to filter your results more quickly
- Redis Cache With GenericJackson2JsonRedisSerializer throws ClassCastException: class java.util.LinkedHashMap cannot be cast to class X #27577
- Redis Cache With GenericJackson2JsonRedisSerializer throws ClassCastException: class java.util.LinkedHashMap cannot be cast to class X #27577
- Comments
- others-how to solve `java.lang.ClassCastException: class java.util.LinkedHashMap cannot be cast to class xxx` when trying to run an kotlin app?
- The error
- The JDK
- The codes that caused the error
- Debug the error
- What does this error mean?
- Other useful information about this error
- 2. Solution
- Solution #1
- Solution #2
- Solution #3
- 3. The code example
- 4. Summary
Saved searches
Use saved searches to filter your results more quickly
You signed in with another tab or window. Reload to refresh your session. You signed out in another tab or window. Reload to refresh your session. You switched accounts on another tab or window. Reload to refresh your session.
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
Redis Cache With GenericJackson2JsonRedisSerializer throws ClassCastException: class java.util.LinkedHashMap cannot be cast to class X #27577
Redis Cache With GenericJackson2JsonRedisSerializer throws ClassCastException: class java.util.LinkedHashMap cannot be cast to class X #27577
Comments
I am trying out basic example of using Redis as Cache provider and trying to customise it to use GenericJackson2JsonRedisSerializer instead of default JdkSerializationRedisSerializer .
When I call the same method which is cached 2nd time then it is throwing error with following stacktrace:
java.lang.ClassCastException: class java.util.LinkedHashMap cannot be cast to class com.example.rediscachedemo.BookmarkDTO (java.util.LinkedHashMap is in module java.base of loader 'bootstrap'; com.example.rediscachedemo.BookmarkDTO is in unnamed module of loader 'app') at com.example.rediscachedemo.BookmarkService$$EnhancerBySpringCGLIB$$d8594d86.getBookmarkById() ~[classes/:na] at com.example.rediscachedemo.BookmarkController.getBookmarkById(BookmarkController.java:19) ~[classes/:na]
With default JdkSerializationRedisSerializer it is working fine.
I also saw few issues opening with similar error but they are happening because of spring-boot-devtools which I am not using.
Is it a bug or am I missing some configuration?
The text was updated successfully, but these errors were encountered:
others-how to solve `java.lang.ClassCastException: class java.util.LinkedHashMap cannot be cast to class xxx` when trying to run an kotlin app?
In this post, I will demonstrate how to solve the error java.lang.ClassCastException: class java.util.LinkedHashMap cannot be cast to class xxx when using kotlin to consume a springboot restful webservice.
The error
Here is the detailed error stacktrace of my error:
14:08:08.403 [main] DEBUG org.springframework.web.client.RestTemplate - HTTP GET http://localhost:8081/msg/ 14:08:08.437 [main] DEBUG org.springframework.web.client.RestTemplate - Accept=[application/json, application/*+json] 14:08:08.482 [main] DEBUG org.springframework.web.client.RestTemplate - Response 200 OK 14:08:08.494 [main] DEBUG org.springframework.web.client.RestTemplate - Reading to [java.util.List] got 3 messages as follows: Exception in thread "main" java.lang.ClassCastException: class java.util.LinkedHashMap cannot be cast to class com.example.domain.Message (java.util.LinkedHashMap is in module java.base of loader 'bootstrap'; com.example.domain.Message is in unnamed module of loader 'app') at com.example.web.WebApplicationKt.test1(WebApplication.kt:22) at com.example.web.WebApplicationKt.main(WebApplication.kt:14) > Task :api-web:bootRun FAILED Execution failed for task ':api-web:bootRun'. > Process 'command '/Library/Java/JavaVirtualMachines/jdk-11.0.17.jdk/Contents/Home/bin/java'' finished with non-zero exit value 1 * Try: > Run with --stacktrace option to get the stack trace. > Run with --info or --debug option to get more log output. > Run with --scan to get full insights.
The JDK
The codes that caused the error
The codes that caused the above error are as follows:
1) The rest controller that provide the restful service:
@RestController class MessageController @GetMapping fun index(): ListMessage> var result = listOf( Message("1","hello1"), Message("2","Object"), Message("3","Privot"), ) return result > >
You can see that this is a very simple rest service that just return a list of objects when you send a http get request to the ‘/’ context.
After start the service, we can test the service via curl :
➜ blog-backend-apis git:(master) ✗ curl http://localhost:8081/msg/ ["id":"1","text":"hello1">,"id":"2","text":"Object">,"id":"3","text":"Privot">]
You can see that the test is fine.
2) The consumer or client of the restful web service
import com.example.domain.Message import org.springframework.web.client.RestTemplate fun test1() val msgs:ListMessage>? = RestTemplate().getForObject("http://localhost:8081/msg/", List::class.java) as ListMessage> if(msgs!=null) println("got $ messages as follows:") for (msg in msgs) //this line cause the error println(msg) > > >
You can see that we just use the RestTemplate to consume the webservice, and convert the result to List .
Run the consumer test function, we got this error:
14:08:08.403 [main] DEBUG org.springframework.web.client.RestTemplate - HTTP GET http://localhost:8081/msg/ 14:08:08.437 [main] DEBUG org.springframework.web.client.RestTemplate - Accept=[application/json, application/*+json] 14:08:08.482 [main] DEBUG org.springframework.web.client.RestTemplate - Response 200 OK 14:08:08.494 [main] DEBUG org.springframework.web.client.RestTemplate - Reading to [java.util.List] got 3 messages as follows: Exception in thread "main" java.lang.ClassCastException: class java.util.LinkedHashMap cannot be cast to class com.example.domain.Message (java.util.LinkedHashMap is in module java.base of loader 'bootstrap'; com.example.domain.Message is in unnamed module of loader 'app') at com.example.web.WebApplicationKt.test1(WebApplication.kt:22) at com.example.web.WebApplicationKt.main(WebApplication.kt:14) > Task :api-web:bootRun FAILED Execution failed for task ':api-web:bootRun'. > Process 'command '/Library/Java/JavaVirtualMachines/jdk-11.0.17.jdk/Contents/Home/bin/java'' finished with non-zero exit value 1 * Try: > Run with --stacktrace option to get the stack trace. > Run with --info or --debug option to get more log output. > Run with --scan to get full insights.
The core error message is:
Exception in thread "main" java.lang.ClassCastException: class java.util.LinkedHashMap cannot be cast to class com.example.domain.Message (java.util.LinkedHashMap is in module java.base of loader 'bootstrap'; com.example.domain.Message is in unnamed module of loader 'app') at com.example.web.WebApplicationKt.test1(WebApplication.kt:22) at com.example.web.WebApplicationKt.main(WebApplication.kt:14)
Debug the error
You can see that the following line caused the error:
And I add a breakpoint at this line to check the real type of msgs , I got this:
You can see that the real data type of msgs is an ArrayList , whose element’s type is LinkedHashMap , not the expected Message object. So the error happened.
What does this error mean?
I think this is an error that caused by the JSON reader(parser) used in the RestTemplate, by default, the springboot framework use the Jackson as the default JSON reader(parser).
Here we tried to convert json array to List :
val msgs:List? = RestTemplate().getForObject("http://localhost:8081/msg/", List::class.java) as List
But this does not work, because we can not specify the real type List to getForObject() .
Jackson reader tried to read the JSON, and then convert to a list, but the generic type is missing, it just use the LinkedHashMap as the element type of the list.
Other useful information about this error
Here is an answer from stackoverflow:
The issue’s coming from Jackson. When it doesn’t have enough information on what class to deserialize to, it uses LinkedHashMap.
Since you’re not informing Jackson of the element type of your ArrayList, it doesn’t know that you want to deserialize into an ArrayList of Accounts. So it falls back to the default.
Instead, you could probably use as(JsonNode.class), and then deal with the ObjectMapper in a richer manner than rest-assured allows. Something like this:
2. Solution
Solution #1
We can just solve the problem by lazy converting the JSON to list, let’t do this job in two steps:
import com.example.domain.Message import com.fasterxml.jackson.core.type.TypeReference import com.fasterxml.jackson.databind.ObjectMapper import org.springframework.web.client.RestTemplate fun test() <> val mapper = ObjectMapper() //jackson parser val msgstr:String? = RestTemplate().getForObject("http://localhost:8081/msg/", String::class.java) // get json string val msgs: ListMessage> = mapper.readValue(msgstr, object : TypeReferenceListMessage>>() <>) //convert json string to List if(msgs!=null) println("got $ messages as follows:") for (msg in msgs) println(msg) > > >
11:08:17.698 [main] DEBUG org.springframework.web.client.RestTemplate - HTTP GET http://localhost:8081/msg/ 11:08:17.713 [main] DEBUG org.springframework.web.client.RestTemplate - Accept=[text/plain, application/json, application/*+json, */*] 11:08:17.866 [main] DEBUG org.springframework.web.client.RestTemplate - Response 200 OK 11:08:17.869 [main] DEBUG org.springframework.web.client.RestTemplate - Reading to [java.lang.String] as "application/json" got 3 messages as follows: Message(id=1, text=hello1) Message(id=2, text=Object) Message(id=3, text=Privot)
The above solution’s key point is to use the ObjectMapper of jackson to convert a JSON string to a kotlin list of messages:
val msgs: ListMessage> = mapper.readValue(msgstr, object : TypeReferenceListMessage>>() <>) //convert json string to List
Pay attention to the parameter object : TypeReference>() <> , it’s an anonymous object with type TypeReference> . If you are java developers, you can understand this by reading the following example:
Thread thread = new Thread() @Override public void run() System.out.println("Hello World"); > >; thread.start();
Anonymous object in kotlin:
val thread = object : Thread() override fun run() println("Hello World") > > thread.start()
Solution #2
For another working solution ,you can use the resttemplate’s getForEntity instead of the getForObject , the detailed steps are as follows:
- Use restTemplate’s getForEntity to get the restful service response
- Get the Array object from the response
- Get the List from Array
fun test_success_2() val responseEntity: ResponseEntityArrayMessage>> = RestTemplate().getForEntity("http://localhost:8081/msg/", ArrayMessage>::class.java) val msgsArray: ArrayMessage> = responseEntity.body!! val msgs: ListMessage> = Arrays.stream(msgsArray) .collect(Collectors.toList()) if(msgs!=null) println("got $ messages as follows:") for (msg in msgs) println(msg) > > >
11:29:10.500 [main] DEBUG org.springframework.web.client.RestTemplate - HTTP GET http://localhost:8081/msg/ 11:29:10.540 [main] DEBUG org.springframework.web.client.RestTemplate - Accept=[application/json, application/*+json] 11:29:10.569 [main] DEBUG org.springframework.web.client.RestTemplate - Response 200 OK 11:29:10.578 [main] DEBUG org.springframework.web.client.RestTemplate - Reading to [com.example.domain.Message[]] got 3 messages as follows: Message(id=1, text=hello1) Message(id=2, text=Object) Message(id=3, text=Privot) Process finished with exit code 0
Solution #3
For the third solution, we can use the RestTemplate’s exchange method, which takes a ParameterizedTypeReference as an parameter to specify the targeting type you want.
- Use RestTemplate’s exchange method to get the reponse, specifying the ParameterizedTypeReference to our type
- Get List from the response body
- Use the result
fun test_success_3() val responseEntity: ResponseEntityListMessage>> = RestTemplate().exchange( "http://localhost:8081/msg/", HttpMethod.GET, null, object : ParameterizedTypeReferenceListMessage>>() <> ) val msgs: ListMessage>? = responseEntity.body if(msgs!=null) println("got $ messages as follows:") for (msg in msgs) println(msg) > > >
You can see that the third solution is very concise and intuitive, the type conversion is happened inside the jackson reader.
3. The code example
The above codes has been uploaded to github, here is the link:
https://github.com/bswen/bswen-blog-backend-apis/blob/main/api-web/src/test/kotlin/tester.kt
4. Summary
In this post, I demonstrated how to solve the java.lang.ClassCastException: class java.util.LinkedHashMap cannot be cast to class xxx when trying to run an kotlin restful client or consumer, the key point is to use the correct JSON to List converter to convert from the JSON to our List. That’s it, thanks for your reading.