Can not deserialize instance of java.lang.String out of START_OBJECT token

JavaDeploymentAtmosphereGrizzly

Java Problem Overview


I'm running into an issue where my deployable jar hits an exception that doesn't happen when I run this locally in IntelliJ.

Exception:

Receiving an event {id=2, socket=0c317829-69bf-43d6-b598-7c0c550635bb, type=getDashboard, data={workstationUuid=ddec1caa-a97f-4922-833f-632da07ffc11}, reply=true}
Firing getDashboard event to Socket#0c317829-69bf-43d6-b598-7c0c550635bb
Failed invoking AtmosphereFramework.doCometSupport()
java.lang.IllegalArgumentException: Can not deserialize instance of java.lang.String out of START_OBJECT token
 at [Source: N/A; line: -1, column: -1]
        at org.codehaus.jackson.map.ObjectMapper._convert(ObjectMapper.java:2502)
        at org.codehaus.jackson.map.ObjectMapper.convertValue(ObjectMapper.java:2468)
        at com.github.flowersinthesand.portal.support.DefaultDispatcher$DefaultHandler$DataParam.resolve(DefaultDispatcher.java:270)
        at com.github.flowersinthesand.portal.support.DefaultDispatcher$DefaultHandler.handle(DefaultDispatcher.java:204)
        at com.github.flowersinthesand.portal.support.DefaultDispatcher.fire(DefaultDispatcher.java:107)
        at com.github.flowersinthesand.portal.support.AbstractSocketFactory.fire(AbstractSocketFactory.java:73)
        at com.github.flowersinthesand.portal.atmosphere.AtmosphereSocketFactory.onRequest(AtmosphereSocketFactory.java:75)
        at org.atmosphere.cpr.AsynchronousProcessor.action(AsynchronousProcessor.java:256)
        at org.atmosphere.cpr.AsynchronousProcessor.suspended(AsynchronousProcessor.java:166)
        at org.atmosphere.container.Grizzly2WebSocketSupport.service(Grizzly2WebSocketSupport.java:75)
        at org.atmosphere.cpr.AtmosphereFramework.doCometSupport(AtmosphereFramework.java:1342)
        at org.atmosphere.websocket.DefaultWebSocketProcessor.dispatch(DefaultWebSocketProcessor.java:219)
        at org.atmosphere.websocket.DefaultWebSocketProcessor$2.run(DefaultWebSocketProcessor.java:183)
        at org.atmosphere.util.VoidExecutorService.execute(VoidExecutorService.java:101)
        at org.atmosphere.websocket.DefaultWebSocketProcessor.dispatch(DefaultWebSocketProcessor.java:178)
        at org.atmosphere.websocket.DefaultWebSocketProcessor.invokeWebSocketProtocol(DefaultWebSocketProcessor.java:167)
        at org.atmosphere.container.Grizzly2WebSocketSupport$Grizzly2WebSocketApplication.onMessage(Grizzly2WebSocketSupport.java:171)
        at org.glassfish.grizzly.websockets.DefaultWebSocket.onMessage(DefaultWebSocket.java:164)
        at org.glassfish.grizzly.websockets.frametypes.TextFrameType.respond(TextFrameType.java:70)
        at org.glassfish.grizzly.websockets.DataFrame.respond(DataFrame.java:104)
        at org.glassfish.grizzly.websockets.WebSocketFilter.handleRead(WebSocketFilter.java:221)
        at org.glassfish.grizzly.filterchain.ExecutorResolver$9.execute(ExecutorResolver.java:119)
        at org.glassfish.grizzly.filterchain.DefaultFilterChain.executeFilter(DefaultFilterChain.java:265)
        at org.glassfish.grizzly.filterchain.DefaultFilterChain.executeChainPart(DefaultFilterChain.java:200)
        at org.glassfish.grizzly.filterchain.DefaultFilterChain.execute(DefaultFilterChain.java:134)
        at org.glassfish.grizzly.filterchain.DefaultFilterChain.process(DefaultFilterChain.java:112)
        at org.glassfish.grizzly.ProcessorExecutor.execute(ProcessorExecutor.java:78)
        at org.glassfish.grizzly.nio.transport.TCPNIOTransport.fireIOEvent(TCPNIOTransport.java:770)
        at org.glassfish.grizzly.strategies.AbstractIOStrategy.fireIOEvent(AbstractIOStrategy.java:112)
        at org.glassfish.grizzly.strategies.WorkerThreadIOStrategy.run0(WorkerThreadIOStrategy.java:115)
        at org.glassfish.grizzly.strategies.WorkerThreadIOStrategy.access$100(WorkerThreadIOStrategy.java:55)
        at org.glassfish.grizzly.strategies.WorkerThreadIOStrategy$WorkerThreadRunnable.run(WorkerThreadIOStrategy.java:135)
        at org.glassfish.grizzly.threadpool.AbstractThreadPool$Worker.doWork(AbstractThreadPool.java:551)
        at org.glassfish.grizzly.threadpool.AbstractThreadPool$Worker.run(AbstractThreadPool.java:531)
        at java.lang.Thread.run(Thread.java:781)
Caused by: org.codehaus.jackson.map.JsonMappingException: Can not deserialize instance of java.lang.String out of START_OBJECT token
 at [Source: N/A; line: -1, column: -1]
        at org.codehaus.jackson.map.JsonMappingException.from(JsonMappingException.java:163)
        at org.codehaus.jackson.map.deser.StdDeserializationContext.mappingException(StdDeserializationContext.java:219)
        at org.codehaus.jackson.map.deser.std.StringDeserializer.deserialize(StringDeserializer.java:44)
        at org.codehaus.jackson.map.deser.std.StringDeserializer.deserialize(StringDeserializer.java:13)
        at org.codehaus.jackson.map.ObjectMapper._readValue(ObjectMapper.java:2704)
        at org.codehaus.jackson.map.ObjectMapper.readValue(ObjectMapper.java:1315)
        at org.codehaus.jackson.map.ObjectMapper._convert(ObjectMapper.java:2498)
        ... 34 more
java.lang.IllegalArgumentException: Can not deserialize instance of java.lang.String out of START_OBJECT token
 at [Source: N/A; line: -1, column: -1] Status 500 Message Server Error

Socket Handler

I believe the exception is occurring when the JSON is parsed into a WorkstationRequest object because of the below item. This is the socket handler:

@On
@Reply
@JsonView({Views.WorkstationView.class})
public WorkstationDashboard getDashboard(@Data WorkstationRequest request) {
    return new WorkstationDashboard(request.getWorkstation());
}

The object the socket handler maps to:

public class WorkstationRequest {

    /* Class to instantiate if this workstation does not already exist */
    private Class<? extends Workstation> workstationClass;

    private WorkflowProcess workflowProcess;

    private PhysicalWorkstation workstation;

    WorkstationService workstationService;

    /**
     * @param workstationClass Required so when jackson maps the UUID we can auto fetch the class
     */
    public WorkstationRequest(Class<? extends Workstation> workstationClass) {
        this.workstationClass = workstationClass;
        workstationService = (WorkstationService) ApplicationContextProvider.getApplicationContext().getBean("workstationService");
    }

    /* Set the workstation based on UUID.  Will register the workstation if it's new */
    @JsonProperty("workstationUuid")
    public void setWorkstation(String workstationUUID) {
        workstation = (PhysicalWorkstation)WorkstationService.getWorkstation(workstationUUID);

        //setup new workstation
        if (workstation == null) {
            WorkstationEntity workstationEntity = workstationService.findByUUID(workstationUUID);
            workstation = (PhysicalWorkstation)Workstation.factory(workstationEntity, workstationClass);

            //register with queue
            WorkflowProcessService.getWorkflowProcess(workstation).registerWorkstation(workstation);
        }
    }

    public PhysicalWorkstation getWorkstation() {
        return workstation;
    }
}

The JSON being mapped:

{"id":2,"socket":"0c317829-69bf-43d6-b598-7c0c550635bb","type":"getDashboard","data":{"workstationUuid":"ddec1caa-a97f-4922-833f-632da07ffc11"},"reply":true}

WorkstationDashboard.java

public class WorkstationDashboard {
    private HashMap<String, Object> queue = new HashMap<String, Object>();

    private LinkedBlockingDeque<JobSetEntity> currentWork;

    public WorkstationDashboard() {
        queue.put("size", 0);
    }

    public WorkstationDashboard(Workstation workstation) {
        fromWorkstation(workstation);
    }

    /* Populate dashboard data from a workstation */
    public void fromWorkstation(Workstation workstation) {
        WorkflowProcess workflowProcess = WorkflowProcessService.getWorkflowProcess(workstation);

        setCurrentWork(workstation.getCurrentWork());
        setQueueSize(workflowProcess.getQueue().size());
    }

    public void setQueueSize(Integer queueSize) {
        queue.put("size", queueSize);
    }

    public HashMap<String, Object> getQueue() {
        return queue;
    }

    public LinkedBlockingDeque<JobSetEntity> getCurrentWork() {
        return currentWork;
    }

    public void setCurrentWork(LinkedBlockingDeque<JobSetEntity> currentWork) {
        this.currentWork = currentWork;
    }
}

I'm at quite a loss as to how to begin debugging this. The stack trace never touches my application. I'm using Maven -> Package to deploy my .jar and executing it with java -jar /path-to-jar.jar

Update: To prevent this question from being incredibly long, I've included my pom.xml here: http://pastebin.com/1ZUtKCfE. I believe this is a dependency issue since the error only occurs on my deployable jar and not on my local PC.

Java Solutions


Solution 1 - Java

You're mapping this JSON

{
    "id": 2,
    "socket": "0c317829-69bf-43d6-b598-7c0c550635bb",
    "type": "getDashboard",
    "data": {
        "workstationUuid": "ddec1caa-a97f-4922-833f-632da07ffc11"
    },
    "reply": true
}

that contains an element named data that has a JSON object as its value. You are trying to deserialize the element named workstationUuid from that JSON object into this setter.

@JsonProperty("workstationUuid")
public void setWorkstation(String workstationUUID) {

This won't work directly because Jackson sees a JSON_OBJECT, not a String.

Try creating a class Data

public class Data { // the name doesn't matter 
    @JsonProperty("workstationUuid")
    private String workstationUuid;
    // getter and setter
}

the switch up your method

@JsonProperty("data")
public void setWorkstation(Data data) {
    // use getter to retrieve it

Solution 2 - Java

If you do not want to define a separate class for nested json , Defining nested json object as JsonNode should work ,for example :

{"id":2,"socket":"0c317829-69bf-43d6-b598-7c0c550635bb","type":"getDashboard","data":{"workstationUuid":"ddec1caa-a97f-4922-833f-632da07ffc11"},"reply":true}

@JsonProperty("data")
	private JsonNode data;

Solution 3 - Java

Data content is so variable, I think the best form is to define it as "ObjectNode" and next create his own class to parse:

Finally:

private ObjectNode data;

Solution 4 - Java

To solve this(in case that your data object can have any undefined json or there is no POJO defined for data), simply use JsonNode to take data as input

private JsonNode data;

Then you can use jsonpath dependency to access the values inside data.

To fetch uuid from this json

  "data": {
    "workStation": {
        {
          "uuid": "2",
          "Title": "Graduation day party"              
        }
    }
    "code": 200
  }

Use JsonContext from JsonPath library to read via JsonPath

DocumentContext jsonContext = JsonContext.createContext(data);
String uuid = jsonContext.read("workStation.uuid");

DocumentContext read method takes the path in json as attribute

Solution 5 - Java

Resolved the problem using Jackson library. Prints are called out of Main class and all POJO classes are created. Here is the code snippets.

MainClass.java

public class MainClass {
  public static void main(String[] args) throws JsonParseException, 
       JsonMappingException, IOException {

String jsonStr = "{\r\n" + "	\"id\": 2,\r\n" + "	\"socket\": \"0c317829-69bf- 
             43d6-b598-7c0c550635bb\",\r\n"
			+ "	\"type\": \"getDashboard\",\r\n" + "	\"data\": {\r\n"
			+ "		\"workstationUuid\": \"ddec1caa-a97f-4922-833f- 
            632da07ffc11\"\r\n" + "	},\r\n"
			+ "	\"reply\": true\r\n" + "}";

	ObjectMapper mapper = new ObjectMapper();

	MyPojo details = mapper.readValue(jsonStr, MyPojo.class);

	System.out.println("Value for getFirstName is: " + details.getId());
	System.out.println("Value for getLastName  is: " + details.getSocket());
	System.out.println("Value for getChildren is: " + 
      details.getData().getWorkstationUuid());
	System.out.println("Value for getChildren is: " + details.getReply());

}

MyPojo.java

public class MyPojo {
    private String id;

    private Data data;

    private String reply;

    private String socket;

    private String type;

	public String getId() {
		return id;
	}

	public void setId(String id) {
		this.id = id;
	}

	public Data getData() {
		return data;
	}

	public void setData(Data data) {
		this.data = data;
	}

	public String getReply() {
		return reply;
	}

	public void setReply(String reply) {
		this.reply = reply;
	}

	public String getSocket() {
		return socket;
	}

	public void setSocket(String socket) {
		this.socket = socket;
	}

	public String getType() {
		return type;
	}

	public void setType(String type) {
		this.type = type;
	} 
}

Data.java

public class Data {
	private String workstationUuid;

	public String getWorkstationUuid() {
		return workstationUuid;
	}

	public void setWorkstationUuid(String workstationUuid) {
		this.workstationUuid = workstationUuid;
	}	
}

RESULTS:

Value for getFirstName is: 2
Value for getLastName  is: 0c317829-69bf-43d6-b598-7c0c550635bb
Value for getChildren is: ddec1caa-a97f-4922-833f-632da07ffc11
Value for getChildren is: true

Solution 6 - Java

This way I solved my problem. Hope it helps others. In my case I created a class, a field, their getter & setter and then provide the object instead of string.

Use this

public static class EncryptedData {
    private String encryptedData;

    public String getEncryptedData() {
        return encryptedData;
    }

    public void setEncryptedData(String encryptedData) {
        this.encryptedData = encryptedData;
    }
}

@PutMapping(value = MY_IP_ADDRESS)
public ResponseEntity<RestResponse> updateMyIpAddress(@RequestBody final EncryptedData encryptedData) {
	try {
		Path path = Paths.get(PUBLIC_KEY);
		byte[] bytes = Files.readAllBytes(path);
		PKCS8EncodedKeySpec ks = new PKCS8EncodedKeySpec(base64.decode(bytes));
		PrivateKey privateKey = KeyFactory.getInstance(CRYPTO_ALGO_RSA).generatePrivate(ks);
		
		Cipher cipher = Cipher.getInstance(CRYPTO_ALGO_RSA);
		cipher.init(Cipher.PRIVATE_KEY, privateKey);
		String decryptedData = new String(cipher.doFinal(encryptedData.getEncryptedData().getBytes()));
		String[] dataArray = decryptedData.split("|");
		
		
		Method updateIp = Class.forName("com.cuanet.client.helper").getMethod("methodName", String.class,String.class);
		updateIp.invoke(null, dataArray[0], dataArray[1]);
		
	} catch (Exception e) {
		LOG.error("Unable to update ip address for encrypted data: "+encryptedData, e);
	}
	
	return null;

Instead of this

@PutMapping(value = MY_IP_ADDRESS)
public ResponseEntity<RestResponse> updateMyIpAddress(@RequestBody final EncryptedData encryptedData) {
	try {
		Path path = Paths.get(PUBLIC_KEY);
		byte[] bytes = Files.readAllBytes(path);
		PKCS8EncodedKeySpec ks = new PKCS8EncodedKeySpec(base64.decode(bytes));
		PrivateKey privateKey = KeyFactory.getInstance(CRYPTO_ALGO_RSA).generatePrivate(ks);
		
		Cipher cipher = Cipher.getInstance(CRYPTO_ALGO_RSA);
		cipher.init(Cipher.PRIVATE_KEY, privateKey);
		String decryptedData = new String(cipher.doFinal(encryptedData.getBytes()));
		String[] dataArray = decryptedData.split("|");
		
		
		Method updateIp = Class.forName("com.cuanet.client.helper").getMethod("methodName", String.class,String.class);
		updateIp.invoke(null, dataArray[0], dataArray[1]);
		
	} catch (Exception e) {
		LOG.error("Unable to update ip address for encrypted data: "+encryptedData, e);
	}
	
	return null;
}

Solution 7 - Java

These types of errors occur when we are using an incorrect Data Type for POJO variables.

Solution 8 - Java

If you're using Kotlin you might wanna try removing data keyword from class declaration.

   {
      "data": {
        "Calendar": {
          "Events": [
            {
              "ID": "1",
              "Title": "Graduation day"
            },
            {
              "ID": "2",
              "Title": "Graduation day party"              
            }
          ]
        }
      }
      "code": 200
    }

I had this as a JSON and my POJO class was like this :

class Response(code: Int, val data: Data){
    @JsonIgnoreProperties(ignoreUnknown = true)
    class Data(@JsonProperty("Calendar") val calendar: Calendar) {}

    @JsonIgnoreProperties(ignoreUnknown = true)
    data class Event(
        val ID: Int? = null,
        val Title: String? = null
    )

    data class Calendar(
        @JsonProperty("Events") val eventsList: ArrayList<Event>
    )
}

Removing data from class Event and class Calendar it worked for me.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionBenView Question on Stackoverflow
Solution 1 - JavaSotirios DelimanolisView Answer on Stackoverflow
Solution 2 - JavaSindhuView Answer on Stackoverflow
Solution 3 - JavarichardhellView Answer on Stackoverflow
Solution 4 - Javadilesh yadavView Answer on Stackoverflow
Solution 5 - JavaAtul Kumar SharmaView Answer on Stackoverflow
Solution 6 - JavaAjayView Answer on Stackoverflow
Solution 7 - JavaDhruv sahuView Answer on Stackoverflow
Solution 8 - JavaNilesh DeokarView Answer on Stackoverflow