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

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.

427865 次浏览

您正在映射这个 JSON

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

它包含一个名为 data的元素,该元素的值为 JSON 对象。您正在尝试将名为 workstationUuid的元素从该 JSON 对象反序列化到这个 setter。

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

这不会直接工作,因为 Jackson 看到的是 JSON _ OBJECT,而不是 String。

尝试创建一个类 Data

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

改变你的方法

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

数据内容是如此多变,我认为最好的形式是将其定义为“ ObjectNode”,然后创建自己的类来进行解析:

最后:

私有 ObjectNode 数据;

如果不想为嵌套的 json 定义一个单独的类,那么将嵌套的 json 对象定义为 JsonNode 应该是可行的,例如:

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


@JsonProperty("data")
private JsonNode data;

利用 Jackson 库解决了这个问题。从 Main 类中调用 Prints,并创建所有 POJO 类。下面是代码片段。

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;
}
}

结果:

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

这样我解决了我的问题,希望能帮到别人。 在我的例子中,我创建了一个类,一个字段,它们的 getter & setter,然后提供对象而不是字符串。

用这个

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;
}

如果你正在使用 Kotlin,你可能想尝试从类声明中移除 data关键字。

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

我把它作为一个 JSON,我的 POJO 类是这样的:

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>
)
}

class Eventclass Calendar移除 data对我很有效。

当我们对 POJO 变量使用不正确的数据类型时,就会发生这些类型的错误。

要解决这个问题(如果您的数据对象可以有任何未定义的 json 或者没有为数据定义的 POJO) ,只需使用 JsonNode将数据作为输入

private JsonNode data;

然后可以使用 Jsonpath 依赖性访问数据中的值。

从这个 Json 那里取回 Uuid

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

使用来自 JsonPath 库的 JsonContext通过 JsonPath 进行读取

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

DocumentContext read 方法将 json 中的路径作为属性