Не вдається десеріалізувати екземпляр java.lang.String з маркера START_OBJECT


81

Я стикаюся з проблемою, коли мій розгортається jar потрапляє до винятку, який не трапляється, коли я запускаю це локально в IntelliJ.

Виняток:

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

Обробник розеток

Я вважаю, що виняток відбувається, коли JSON аналізується в об'єкт WorkstationRequest через наведений нижче елемент. Це обробник сокета:

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

Об’єкт, який обробник сокета відображається у:

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

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

Я досить заблукав, як почати це налагоджувати. Трасування стека ніколи не торкається мого додатка. Я використовую Maven -> Packageдля розгортання свого .jar та його виконання за допомогоюjava -jar /path-to-jar.jar

Оновлення: Щоб запобігти тому, щоб це питання було неймовірно довгим, я включив тут свій pom.xml: http://pastebin.com/1ZUtKCfE . Я вважаю, що це проблема залежності, оскільки помилка виникає лише в моєму розгортається jar, а не на моєму локальному ПК.

Відповіді:


76

Ви накладаєте цей JSON на карту

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

що містить елемент з іменем, значенням dataякого є об’єкт JSON. Ви намагаєтесь десеріалізувати елемент, названий workstationUuidз цього об’єкта JSON, у цьому сеттері.

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

Це не буде працювати безпосередньо, оскільки Джексон бачить JSON_OBJECT, а не рядок.

Спробуйте створити клас 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

Мені не вдалося детально описати це, але бібліотека Socket, яку я використовую, відображає dataполе в цьому масиві на мій об'єкт через public WorkstationDashboard getDashboard(@Data WorkstationRequest request) {. Він викликає getDashboard()через typeв тому JSON, потім карти data. Я можу помилитися, оскільки я насправді не знаю, що відбувається. Але оскільки все це працює локально на моєму ПК і ламається, коли я розгортаю свою програму через .jar, здається, це якась проблема залежності. Мій пом тут: pastebin.com/1ZUtKCfE
Webnet

1
@Webnet Чи @Dataє власною анотацією, яка пов'язує dataполе з JSON? Вам доведеться повернутися назад, щоб побачити, як це породжує WorkstationRequestаргумент.
Sotirios Delimanolis

@SotiriosDelimanolis, ТВЕРДА відповідь. Врятував мене години пошуку та усунення несправностей.
icfantv

@SotiriosDelimanolis, якщо мої дані постійно змінюватимуться, я маю на увазі, що я не був би впевнений, скільки значень буде dataмістити поле. Чи є якась робота навколо? stackoverflow.com/questions/39053106 / ...
theGamblerRises

3
@theGamblerRises Якщо ви абсолютно не уявляєте, який тип буде з ним пов'язаний, ви можете використовувати загальний тип JSON для використовуваної вами бібліотеки JSON. З Джексоном, тобто JsonNode. Однак це поєднує ваш код із Джексоном і не полегшує використання цього значення.
Sotirios Delimanolis

29

Якщо ви не хочете визначати окремий клас для вкладеного 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;

Мені не потрібно було використовувати анотацію @JsonProperty - з мого розуміння, це потрібно лише там, де ім'я в JSON відрізняється від імені атрибута в Object.
Дагмар

Якщо ви не хочете використовувати 'JsonNode', ви можете визначити окремий клас для масиву '"data": {..}' і використовувати це ім'я класу замість 'JsonNode'.
hipokito

25

Вміст даних настільки мінливий, що, на мою думку, найкращою формою є визначити його як "ObjectNode" і далі створити власний клас для синтаксичного аналізу:

Нарешті:

приватні дані ObjectNode;


3
Ти врятував для цього мою довгу погоню. Моє dataполе може мати будь-який JSON всередині, тому я не можу зіставити його з деяким POJO. Я шукав якесь рішення, яке може містити лише значення JSON. Дуже дякую. Щоб ще раз перевірити, чи є побічний ефект чи недоліки його використання?
theGamblerRises

1

Вирішено проблему за допомогою бібліотеки Джексона. Відбитки викликаються з основного класу і створюються всі класи 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;
    }   
}

РЕЗУЛЬТАТИ:

Значення для getFirstName: 2
Значення для getLastName: 0c317829-69bf-43d6-b598-7c0c550635bb
Значення для getChildren: ddec1caa-a97f-4922-833f-632da07ffc11
Значення для getChildren: true

1

Таким чином я вирішив свою проблему. Сподіваюся, це допомагає іншим. У моєму випадку я створив клас, поле, їх 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;

Замість цього

@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;
}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.