Can not deserialize instance of java.util.HashMap out of START_ARRAY token

Sun, Jul 26, 2020

Read in 4 minutes

Introduction :

Table of content :

1. Introduction to REST API
2. Create a spring boot project
3. POM.xml dependencies added 
4. Rest controller and other annotation used
5. Possible Errors and Exceptions
6. Conclusion 

Introduction to REST API :

Everyone knows that REST getting more popular nowadays and all the companies started building the REST API’s for their business layer. In this blog we will develop spring boot REST service which will connect with the third party REST API to get the data.

For the Third party REST API, I use this https://jsonplaceholder.typicode.com/todos/ rest endpoint. This endpoint gives you array of JSON data.

Create Spring boot Project :

To create a spring boot project, you can use Spring suite tool or you can install spring boot plugin in your eclipse. For new spring boot project clicck on file -> new -> spring starter project.

spring

If you want more details on how to create spring boot project, you can refer here Springboot Starter

POM.xml dependencies added :

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.6.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.theprogrammerguide</groupId>
<artifactId>ConsumeRESTAPI</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>ConsumeRESTAPI</name>
<description>Demo project for Spring Boot rest service </description>

<properties>
<java.version>1.8</java.version>
</properties>

<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>

<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>

</project>

From the above dependencies, the spring-boot-starter-web is to develop web application. Here the tomcat server is embedded with spring boot project so, so you run in local and test the service with local host.

Rest Controller and other annotations used :

@RestController- This is the combination of @controller and @responsebody 

@Bean  - explicitly declare a single bean 

@Value - get the value mentioned the application.properties file 

@Autowired - is for dependency injection 

@GetMapping. This is the shortcut for  @RequestMapping(method = RequestMethod.GET) and using this annotation we can define endpoint for our rest service. In the below program I have added as @GetMapping("/getDetails") so, the enpoint for my service will be http://localhost:8080/getDetails 
@RestController
public class ProductRestController {
private static final Logger log = LoggerFactory.getLogger(ProductRestController.class);
@Value("${endpoint}")
String endpoint;
@Autowired
RestTemplate restTemplate;
@Bean
public RestTemplate restTemplate(RestTemplateBuilder builder) {
return builder.build();
}
	
@GetMapping("/getDetails")
public List<HashMap<String, Object>> getDetails()
throws URISyntaxException, JsonMappingException, JsonProcessingException {
HttpHeaders headers = new HttpHeaders();
headers.add("user-agent", "Application");
HttpEntity<String> entity = new HttpEntity<>(headers);
log.info("endpoint " + endpoint);
ResponseEntity<String> responseEntity = restTemplate.exchange(endpoint, HttpMethod.GET, entity, String.class);
String data = responseEntity.getBody();
System.out.println("data ==  " + data);
ObjectMapper mapper = new ObjectMapper();
List<HashMap<String, Object>> dataAsMap = mapper.readValue(data, List.class);
return dataAsMap;

}
}

Code Explanation:

To consume the rest api, here were are using “restTemplate.exchange(endpoint, HttpMethod.GET, entity, String.class)” and we are getting the resposne boody as string. Now have to convert the resposne body to “HashMap” to return resposne as key value pair as JSON response. To convert the string to HashMap we are using ObjectMapper. This piece of code mapper.readValue(data, List.class) will convert the string to a HashMap type.

And the respose data what I am receiving form the endpoint “https://jsonplaceholder.typicode.com/todos/" is array of objects and one Hasmmap object is not enough. So I am declaring as List<HashMap<String, Object» as a type of dataAsMap and also returning the same.So the return type of this method also List<HashMap<String, Object».

Possible Errors and Exceptions:

As mentioned in the “code explanation” , we are receiving array of objects from the endpoint and to handle that we should convert that resposne as list of HashMap.If you are using HashMap<String, Object> as type of the “dataAsMap” instead of List<HashMap<String, Object> you will get the below error.

Can not deserialize instance of java.util.HashMap out of START_ARRAY token.

Conclusion :

In this article, I am just writing all the business logic in the controller itself. But as per the standard, you have to separate the business logic as service class and you can call the service class method in controller class.