Instanz von java.lang.String kann nicht aus START_OBJECT-Token deserialisiert werden


76

Ich stoße auf ein Problem, bei dem mein bereitstellbares JAR auf eine Ausnahme stößt, die nicht auftritt, wenn ich dies lokal in IntelliJ ausführe.

Ausnahme:

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

Ich glaube, die Ausnahme tritt auf, wenn der JSON aufgrund des folgenden Elements in ein WorkstationRequest-Objekt analysiert wird. Dies ist der Socket-Handler:

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

Das Objekt, dem der Socket-Handler zugeordnet ist:

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

Der zugeordnete JSON:

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

Ich bin ziemlich ratlos, wie ich mit dem Debuggen beginnen soll. Die Stapelverfolgung berührt meine Anwendung nie. Ich verwende Maven -> Packagemeine .jar-Datei und führe sie mit ausjava -jar /path-to-jar.jar

Update: Um zu verhindern, dass diese Frage unglaublich lang wird, habe ich meine pom.xml hier eingefügt: http://pastebin.com/1ZUtKCfE . Ich glaube, dies ist ein Abhängigkeitsproblem, da der Fehler nur auf meinem bereitstellbaren JAR und nicht auf meinem lokalen PC auftritt.

Antworten:


73

Sie ordnen diesen JSON zu

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

das ein Element mit dem Namen enthält, datadessen Wert ein JSON-Objekt hat. Sie versuchen, das workstationUuidvon diesem JSON-Objekt benannte Element in diesen Setter zu deserialisieren .

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

Dies funktioniert nicht direkt, da Jackson ein JSON_OBJECT und keinen String sieht.

Versuchen Sie, eine Klasse zu erstellen Data

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

das schalten Sie Ihre Methode auf

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

Ich konnte dies nicht im Detail beschreiben, aber die von mir verwendete Socket-Bibliothek ordnet das dataFeld in diesem Array über meinem Objekt zu public WorkstationDashboard getDashboard(@Data WorkstationRequest request) {. Es ruft getDashboard()wegen des typein diesem JSON auf und ordnet dann zu data. Ich könnte mich irren, da ich nicht wirklich weiß, was los ist. Da dies alles lokal auf meinem PC funktioniert und beim Bereitstellen meiner Anwendung über eine .jar-Datei unterbrochen wird, scheint es sich um ein Abhängigkeitsproblem zu handeln. Mein Pom ist hier: pastebin.com/1ZUtKCfE
Webnet

1
@Webnet Ist @Dataeine benutzerdefinierte Anmerkung, die das dataFeld vom JSON bindet ? Sie müssen zurückverfolgen, um zu sehen, wie das WorkstationRequestArgument generiert wird .
Sotirios Delimanolis

@SotiriosDelimanolis, FESTE Antwort Mann. Ersparte mir Stunden bei der Fehlerbehebung.
icfantv

@SotiriosDelimanolis, wenn sich meine Daten ständig ändern, wäre ich mir nicht sicher, wie viele Werte das dataFeld enthalten wird. Gibt es irgendwelche Probleme? stackoverflow.com/questions/39053106/…
theGamblerRises

3
@theGamblerRises Wenn Sie absolut keine Ahnung haben, welcher Typ damit verknüpft wird, können Sie den generischen JSON-Typ für die von Ihnen verwendete JSON-Bibliothek verwenden. Bei Jackson also JsonNode. Dies koppelt Ihren Code jedoch mit Jackson und macht es nicht einfach, diesen Wert dann zu verwenden.
Sotirios Delimanolis

28

Wenn Sie keine separate Klasse für verschachtelten JSON definieren möchten, sollte das Definieren eines verschachtelten JSON-Objekts als JsonNode beispielsweise funktionieren:

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

@JsonProperty("data")
    private JsonNode data;

Ich musste die Annotation @JsonProperty nicht verwenden. Nach meinem Verständnis ist sie nur erforderlich, wenn sich der Name im JSON vom Attributnamen im Objekt unterscheidet.
Dagmar

Wenn Sie 'JsonNode' nicht verwenden möchten, können Sie eine separate Klasse für das Array '"data": {..}' definieren und diesen Klassennamen anstelle von 'JsonNode' verwenden.
Hipokito

25

Der Dateninhalt ist so variabel, dass ich denke, die beste Form ist, ihn als "ObjectNode" zu definieren und als nächstes seine eigene Klasse zum Parsen zu erstellen:

Schließlich:

private ObjectNode-Daten;


3
Du hast meine lange Jagd dafür gerettet. In meinem dataFeld kann sich ein beliebiger JSON befinden, daher kann ich ihn keinem POJO zuordnen. Ich habe nach einer Lösung gesucht, die nur JSON-Werte enthalten kann. Ich danke dir sehr. Gibt es irgendwelche Nebeneffekte oder Nachteile bei der Verwendung?
TheGamblerRises

1

Das Problem mit der Jackson-Bibliothek wurde behoben. Drucke werden aus der Hauptklasse aufgerufen und alle POJO-Klassen werden erstellt. Hier sind die Codefragmente.

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

ERGEBNISSE:

Der Wert für getFirstName ist: 2
Der Wert für getLastName lautet: 0c317829-69bf-43d6-b598-7c0c550635bb
Der Wert für getChildren lautet: ddec1caa-a97f-4922-833f-632da07ffc11
Der Wert für getChildren ist: true

1

Auf diese Weise habe ich mein Problem gelöst. Hoffe es hilft anderen. In meinem Fall habe ich eine Klasse, ein Feld, ihren Getter & Setter erstellt und dann das Objekt anstelle einer Zeichenfolge bereitgestellt.

Benutze das

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;

An Stelle von

@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;
}
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.