jsonspringspring-bootjunitjackson

JsonParseException: Unexpected character ('s' (code 115)): was expecting double-quote to start field name


I am trying to test my services using Junit and Mockito. I am able to mock the service and get the response from the Resource class. The response body is having some data of JSON format which is of the object type. I am trying to get the data from the object using ObjectMapper of Jackson library using spring boot.

EmployeeResource.java

package com.ems.resource;

import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.jms.core.JmsTemplate;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;

import com.ems.exception.InvalidInputDataException;
import com.ems.service.EmployeeService;
import com.ems.service.UserService;
import com.ems.util.EmailUtils;
import com.ms.base.beans.EmailObject;
import com.ms.base.entity.Employee;
import com.ms.base.entity.User;

@RestController
@RequestMapping(path = "/employee")
public class EmployeeResource 
{

    final Logger logger = LogManager.getLogger(EmployeeResource.class);
    final String EMAIL_QUEUE_NAME = "EmailQueue";
            
    @Autowired
    JmsTemplate jmsTemplate;
    
    @Autowired
    EmployeeService employeeService;
    
    @Autowired 
    UserService userService;
    
    @RequestMapping(path = "/list", method = {RequestMethod.GET, RequestMethod.POST})
    @PreAuthorize("hasAnyRole('ROLE_ADMIN', 'ROLE_HR', 'ROLE_EMP_CRUD')")
    public ResponseEntity<Object> listEmployees() throws Exception
    {
        Map responseMap = new HashMap();
        List<Employee> employees = employeeService.getEmployees();
        
        responseMap.put("employees", employees);
        responseMap.put("status", HttpStatus.OK.value());
        responseMap.put("statusName", HttpStatus.OK.name());
        
        return new ResponseEntity(responseMap, HttpStatus.OK);
    }
}

EmployeeService.java

package com.ems.service;

import java.util.Date;
import java.util.List;
import java.util.stream.Collectors;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.ems.repository.EmployeeRepository;
import com.ms.base.entity.Employee;

@Service
public class EmployeeService 
{

    @Autowired
    EmployeeRepository employeeRepository;
    
    @Autowired
    UserService userService;
    
    
    public List<Employee> getEmployees()
    {
        return employeeRepository.findAll();
    }
}

EmployeeResourceTest.java

package com.ems.resource;

import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
import java.util.Map;

import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.junit.platform.runner.JUnitPlatform;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import org.springframework.http.ResponseEntity;

import com.ems.service.EmployeeService;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.google.gson.JsonObject;
import com.ms.base.entity.Employee;

@RunWith(JUnitPlatform.class)
@ExtendWith(MockitoExtension.class)
public class EmployeeResourceTest 
{

    @InjectMocks
    private EmployeeResource employeeResource;
    
    @Mock
    private EmployeeService employeeService;
    
    
    @Test 
    public void getAllEmployees() throws Exception
    {
        
        
         // given
        Employee employee1 = new Employee();
        employee1.setId(1L);
        employee1.setName("Vineel Pellella");
        employee1.setDateOfBirth(new Date(780537600));
        employee1.setDateOfJoining(new Date());
        employee1.setDesignation("ROLE_ADMIN");
        employee1.setWorkLocation("Hyderabad");
        employee1.setContactNo(9985896040L);
        employee1.setEmail("howtodoinjava@gmail.com");

        List<Employee> employees = new ArrayList<Employee>();
        employees.addAll(Arrays.asList(employee1));
 
        when(employeeService.getEmployees()).thenReturn(employees);
 
        // when
        ResponseEntity<Object> result = employeeResource.listEmployees();
        
        // then
        ObjectMapper objectMapper = new ObjectMapper();
        objectMapper.configure(JsonParser.Feature.ALLOW_UNQUOTED_FIELD_NAMES, true);
        objectMapper.configure(SerializationFeature.INDENT_OUTPUT, true);
        
        JsonParser parser = objectMapper.createParser(result.getBody().toString());
        
        Map respMap = objectMapper.readValue(parser, Map.class);
//        System.out.println(emp.getDateOfBirth());
        assertThat(result.getBody()).isNotNull();
        verify(employeeResource).listEmployees();
        verify(employeeService).getEmployees();
        
         
    }
    
}

Employee.java

package com.ms.base.entity;

import java.io.Serializable;
import java.util.Date;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;

import lombok.Data;
import lombok.NoArgsConstructor;

@Entity
@Data
@NoArgsConstructor
public class Employee implements Serializable
{
    private static final long serialVersionUID = -3732596549369515261L;

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;    
    
    private String name;
    private String designation;
    private Date dateOfBirth;
    private Date dateOfJoining;
    private String workLocation;

    @Column(unique = true)
    private Long contactNo;
    
    @Column(unique = true)
    private String email;
    private Date dateOfLeaving;
    

}

The employeeRepository is just an interface of JPARepository.

StackTrace

com.fasterxml.jackson.core.JsonParseException: Unexpected character ('s' (code 115)): was expecting double-quote to start field name
 at [Source: (String)"{statusName=OK, employees=[Employee(id=1, name=Vineel Pellella, designation=null, dateOfBirth=Sat Jan 10 06:18:57 IST 1970, dateOfJoining=Mon Sep 27 15:34:45 IST 2021, workLocation=Hyderabad, contactNo=9985896040, email=howtodoinjava@gmail.com, dateOfLeaving=null)], status=200}"; line: 1, column: 3]
    at com.fasterxml.jackson.core.JsonParser._constructError(JsonParser.java:2337)
    at com.fasterxml.jackson.core.base.ParserMinimalBase._reportError(ParserMinimalBase.java:710)
    at com.fasterxml.jackson.core.base.ParserMinimalBase._reportUnexpectedChar(ParserMinimalBase.java:635)
    at com.fasterxml.jackson.core.json.ReaderBasedJsonParser._handleOddName(ReaderBasedJsonParser.java:1814)
    at com.fasterxml.jackson.core.json.ReaderBasedJsonParser.nextFieldName(ReaderBasedJsonParser.java:941)
    at com.fasterxml.jackson.databind.deser.std.MapDeserializer._readAndBindStringKeyMap(MapDeserializer.java:582)
    at com.fasterxml.jackson.databind.deser.std.MapDeserializer.deserialize(MapDeserializer.java:437)
    at com.fasterxml.jackson.databind.deser.std.MapDeserializer.deserialize(MapDeserializer.java:32)
    at com.fasterxml.jackson.databind.deser.DefaultDeserializationContext.readRootValue(DefaultDeserializationContext.java:322)
    at com.fasterxml.jackson.databind.ObjectMapper._readMapAndClose(ObjectMapper.java:4593)
    at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:3548)
    at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:3516)
    at com.ems.resource.EmployeeResourceTest.getAllEmployees(EmployeeResourceTest.java:62)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
    at java.lang.reflect.Method.invoke(Unknown Source)
    at org.junit.platform.commons.util.ReflectionUtils.invokeMethod(ReflectionUtils.java:688)
    at org.junit.jupiter.engine.execution.MethodInvocation.proceed(MethodInvocation.java:60)
    at org.junit.jupiter.engine.execution.InvocationInterceptorChain$ValidatingInvocation.proceed(InvocationInterceptorChain.java:131)
    at org.junit.jupiter.engine.extension.TimeoutExtension.intercept(TimeoutExtension.java:149)
    at org.junit.jupiter.engine.extension.TimeoutExtension.interceptTestableMethod(TimeoutExtension.java:140)
    at org.junit.jupiter.engine.extension.TimeoutExtension.interceptTestMethod(TimeoutExtension.java:84)
    at org.junit.jupiter.engine.execution.ExecutableInvoker$ReflectiveInterceptorCall.lambda$ofVoidMethod$0(ExecutableInvoker.java:115)
    at org.junit.jupiter.engine.execution.ExecutableInvoker.lambda$invoke$0(ExecutableInvoker.java:105)
    at org.junit.jupiter.engine.execution.InvocationInterceptorChain$InterceptedInvocation.proceed(InvocationInterceptorChain.java:106)
    at org.junit.jupiter.engine.execution.InvocationInterceptorChain.proceed(InvocationInterceptorChain.java:64)
    at org.junit.jupiter.engine.execution.InvocationInterceptorChain.chainAndInvoke(InvocationInterceptorChain.java:45)
    at org.junit.jupiter.engine.execution.InvocationInterceptorChain.invoke(InvocationInterceptorChain.java:37)
    at org.junit.jupiter.engine.execution.ExecutableInvoker.invoke(ExecutableInvoker.java:104)
    at org.junit.jupiter.engine.execution.ExecutableInvoker.invoke(ExecutableInvoker.java:98)
    at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.lambda$invokeTestMethod$6(TestMethodTestDescriptor.java:210)
    at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
    at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.invokeTestMethod(TestMethodTestDescriptor.java:206)
    at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:131)
    at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:65)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$5(NodeTestTask.java:139)
    at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$7(NodeTestTask.java:129)
    at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:137)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:127)
    at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:126)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:84)
    at java.util.ArrayList.forEach(Unknown Source)
    at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.invokeAll(SameThreadHierarchicalTestExecutorService.java:38)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$5(NodeTestTask.java:143)
    at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$7(NodeTestTask.java:129)
    at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:137)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:127)
    at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:126)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:84)
    at java.util.ArrayList.forEach(Unknown Source)
    at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.invokeAll(SameThreadHierarchicalTestExecutorService.java:38)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$5(NodeTestTask.java:143)
    at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$7(NodeTestTask.java:129)
    at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:137)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:127)
    at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:126)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:84)
    at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.submit(SameThreadHierarchicalTestExecutorService.java:32)
    at org.junit.platform.engine.support.hierarchical.HierarchicalTestExecutor.execute(HierarchicalTestExecutor.java:57)
    at org.junit.platform.engine.support.hierarchical.HierarchicalTestEngine.execute(HierarchicalTestEngine.java:51)
    at org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:108)
    at org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:88)
    at org.junit.platform.launcher.core.EngineExecutionOrchestrator.lambda$execute$0(EngineExecutionOrchestrator.java:54)
    at org.junit.platform.launcher.core.EngineExecutionOrchestrator.withInterceptedStreams(EngineExecutionOrchestrator.java:67)
    at org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:52)
    at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:96)
    at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:75)
    at org.junit.platform.runner.JUnitPlatform.run(JUnitPlatform.java:139)
    at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:89)
    at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:41)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:542)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:770)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:464)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:210)

Result of result.getBody().toString()

{statusName=OK, employees=[Employee(id=1, name=Vineel Pellella, designation=ROLE_ADMIN, dateOfBirth=Sat Jan 10 06:18:57 IST 1970, dateOfJoining=Mon Sep 27 17:22:09 IST 2021, workLocation=Hyderabad, contactNo=9985896040, email=howtodoinjava@gmail.com, dateOfLeaving=null), Employee(id=2, name=Vineel Pellella2, designation=ROLE_ADMIN, ROLE_HR, dateOfBirth=Sat Jan 10 06:18:57 IST 1970, dateOfJoining=Mon Sep 27 17:22:09 IST 2021, workLocation=Bengaluru, contactNo=9985896041, email=vineel1@gmail.com, dateOfLeaving=null), Employee(id=3, name=Vineel Pellella3, designation=ROLE_ADMIN, ROLE_HR, dateOfBirth=Sat Jan 10 06:18:57 IST 1970, dateOfJoining=Mon Sep 27 17:22:09 IST 2021, workLocation=Chennai, contactNo=9985896042, email=vineel2@gmail.com, dateOfLeaving=null)], status=200}

Solution

  • After long research, I tried the below approach which is automatically converting the invalid JSON to valid JSON.

    ObjectMapper objectMapper = new ObjectMapper();
    String respData = objectMapper.writeValueAsString(result.getBody());
    Map respMap = objectMapper.readValue(respData, Map.class);
    

    On situations where the JSON is in a Java String and you want it back to another String this does the same conversion:

    ObjectMapper objectMapper = new ObjectMapper();
    objectMapper.configure(Feature.ALLOW_UNQUOTED_FIELD_NAMES, true);
    JsonNode jsonNode = objectMapper.readTree(jsonToReform);        
    String reformedJson = objectMapper.writeValueAsString(jsonNode);