Java Rest Client Problem with JSON deserialization

Dear fellow Camundians,

Since the upgrade of my project to Camunda BMP 7.5 i have a problem with my (admittedly simple) REST Client. I am trying to read all Filters via REST from the Engine. In V 7.2 this worked, now it does not anymore…

The property “query” in FilterDto changed from V7.2 to V7.5 from “org.codehaus.jackson.JsonNode”
to “AbstractQueryDto<?> query”

Any ideas, how i can deal with this? (Client code and Exception below)

Thank you in advance…
:frowning:
Helmut

Client code (short version) using RestEasy

    public List<FilterDto> getFilterForOwner(String owner) {
        List<FilterDto> retList = new ArrayList<FilterDto>(0);
        try {
            target = client.target(baseUrl + "/filter");
            GenericType<List<FilterDto>> list = new GenericType<List<FilterDto>>() {};
            retList = target.request(MediaType.APPLICATION_JSON).get(list);
        } catch (Exception e) {
            logger.error("Could not retrieve filter list for owner '" + owner + "'");
        }
        return retList;
    } 

Exception (also short version… :wink: )

Caused by: org.codehaus.jackson.map.JsonMappingException: Can not construct instance of org.camunda.bpm.engine.rest.dto.AbstractQueryDto, problem: abstract types can only be instantiated with additional type information
 at [Source: org.apache.http.conn.EofSensorInputStream@2f217633; line: 1, column: 101] (through reference chain: org.camunda.bpm.engine.rest.dto.runtime.FilterDto["query"])
    at org.codehaus.jackson.map.JsonMappingException.from(JsonMappingException.java:163)
    at org.codehaus.jackson.map.deser.StdDeserializationContext.instantiationException(StdDeserializationContext.java:233)
    at org.codehaus.jackson.map.deser.AbstractDeserializer.deserialize(AbstractDeserializer.java:60)

just taking a shot in the dark based on the docs:
https://docs.camunda.org/javadoc/camunda-bpm-platform/7.5/org/camunda/bpm/engine/rest/dto/AbstractQueryDto.html

What happens in you use toString()? Whats your output as a string? and can you convert to JSON once it is a string?

Hi Stephen,

thank’s for your answer. The response of the camunda rest engine in V7.5 is (except for a few white spaces) the same as in V7.2. The problem is the deserialization of the JSON-String. When i change my test case to receive just the jason string and try to deserialize “manually” i get the same exception.

Other version of test case: get string response instead of object list

String variableString = target.request(MediaType.APPLICATION_JSON).get(String.class);
System.out.println(variableString);

FilterDto[] taskDtoArray = gson.fromJson(variableString, FilterDto[].class);
System.out.println("taskDtoArray:" + taskDtoArray);

JSON-Response from Camunda engine

[{"id":"00df7ad8-4d7f-11e6-9336-6c0b84677bce","resourceType":"Task","name":"My Tasks","owner":"demo","query":{"assigneeExpression":"${currentUser()}","taskVariables":[],"processVariables":[],"caseInstanceVariables":[]},"properties":{"variables":[{"name":"amount","label":"Invoice Amount"},{"name":"invoiceNumber","label":"Invoice Number"},{"name":"creditor","label":"Creditor"},{"name":"approver","label":"Approver"}],"description":"Tasks assigned to me","priority":-10}},{"id":"00e03e2a-4d7f-11e6-9336-6c0b84677bce","resourceType":"Task","name":"My Group Tasks","owner":"demo","query":{"unassigned":true,"candidateGroupsExpression":"${currentUserGroups()}","taskVariables":[],"processVariables":[],"caseInstanceVariables":[]},"properties":{"variables":[{"name":"amount","label":"Invoice Amount"},{"name":"invoiceNumber","label":"Invoice Number"},{"name":"creditor","label":"Creditor"},{"name":"approver","label":"Approver"}],"description":"Tasks assigned to my Groups","priority":-5}},{"id":"00e176b7-4d7f-11e6-9336-6c0b84677bce","resourceType":"Task","name":"All Tasks","owner":null,"query":{"taskVariables":[],"processVariables":[],"caseInstanceVariables":[]},"properties":{"variables":[{"name":"amount","label":"Invoice Amount"},{"name":"invoiceNumber","label":"Invoice Number"},{"name":"creditor","label":"Creditor"},{"name":"approver","label":"Approver"}],"color":"#555555","showUndefinedVariable":false,"description":"All Tasks - Not recommended to be used in production :)","refresh":false,"priority":10}}]

"Opinion" of Gson :frowning:

    at com.google.gson.internal.ConstructorConstructor$3.construct(ConstructorConstructor.java:107)
    at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$Adapter.read(ReflectiveTypeAdapterFactory.java:162)
    at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$1.read(ReflectiveTypeAdapterFactory.java:93)
    at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$Adapter.read(ReflectiveTypeAdapterFactory.java:172)
    at com.google.gson.internal.bind.TypeAdapterRuntimeTypeWrapper.read(TypeAdapterRuntimeTypeWrapper.java:40)
    at com.google.gson.internal.bind.ArrayTypeAdapter.read(ArrayTypeAdapter.java:72)
    at com.google.gson.Gson.fromJson(Gson.java:803)
    at com.google.gson.Gson.fromJson(Gson.java:768)
    at com.google.gson.Gson.fromJson(Gson.java:717)
    at com.google.gson.Gson.fromJson(Gson.java:689)
    at at.immodat.bpm.main.data.BpmRestClient.getFilterForOwner(BpmRestClient.java:124)
    at at.immodat.bpm.main.data.test.BpmRestClientTest.testGetFilterForOwner(BpmRestClientTest.java:48)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:497)
    at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:47)
    at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
    at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:44)
    at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
    at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:271)
    at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:70)
    at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:50)
    at org.junit.runners.ParentRunner$3.run(ParentRunner.java:238)
    at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:63)
    at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:236)
    at org.junit.runners.ParentRunner.access$000(ParentRunner.java:53)
    at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:229)
    at org.junit.runners.ParentRunner.run(ParentRunner.java:309)
    at org.junit.runner.JUnitCore.run(JUnitCore.java:160)
    at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:119)
    at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:42)
    at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:234)
    at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:74)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:497)
    at com.intellij.rt.execution.application.AppMain.main(AppMain.java:144)
Caused by: java.lang.InstantiationException
    at sun.reflect.InstantiationExceptionConstructorAccessorImpl.newInstance(InstantiationExceptionConstructorAccessorImpl.java:48)
    at java.lang.reflect.Constructor.newInstance(Constructor.java:422)
    at com.google.gson.internal.ConstructorConstructor$3.construct(ConstructorConstructor.java:104)
    ... 38 more

Hi @Helmut_Schuster,

I advise against using the engine-rest Java classes for Java REST client libraries. The reason is that these classes and their structure may change any time since they are not part of the Camunda public API. This problem is a result of this.

When you look at FilterDto, you see that the setter has some Jackson annotations that tell Jackson which class to instantiate when the request is deserialized. Perhaps you can do something similar with Gson.

Anyway, I suggest you generate custom DTO classes that you map the responses to.

Cheers,
Thorben

Hi Thorben,

Thank’s for your advice. I will try following solutions:

  • Maybe i can do the switch in the RestEasy client from Gson to Jackson and further use the Camunda Dto’s (on my risk! :wink: )

  • If the above does not work out, i will build my own DTO’s.

Once again: Thank you all for your quick replies…

:slight_smile:
Helmut