Accessing Variable using Camunda Java / Spring Boot Client

I’m trying to access process variables using Camunda Client. After creation of the request and retrieval of the response I end up with a list of variable wrapper:

val items: List<Variable> = variableRequest.send().join().items()

The Variable interface gives me the variable name and value (along with other attributes). The value is of type String and contains as a value:

  • Quoted String if the variable is of type string → “stringValue”
  • Number as a string if the variable is a number → 127
  • true / false string if the variable is a boolean → true

I want to use the values directly without constructing correct Java types of them. Is there any utility I can use for this. My native approach of using Jackson ObjectMapper of the map associated from this list by variable name fails and effectively always delivers me Map<String, String> instead of Map<String, Object>.

Cheers,

Simon

The Camunda Java Client provides utility methods like getVariablesAsType(YourClass.class) and getVariablesAsMap() for proper type conversion instead of manually parsing string values. I found the following relevant resources:

Does this help? If not, can anyone from the community jump in? :waving_hand:


:light_bulb: Hints: Use the Ask AI feature in Camunda’s documentation to chat with AI and get fast help. Report bugs and features in Camuda’s GitHub issue tracker. Trust the process. :robot:

This is only true for Worker-retrieved (legacy) ActivatedJobs, but not for modern User Tasks. I’m more interested in the User Task Variables. Seem to be the same problem as described in. Incorrect representation of string process variable - #5 by jonathan.lukas

Hi @zambrovski ,

I guess this is what you are looking for: feat: allow to get variable value as type by jonathanlukas · Pull Request #48795 · camunda/camunda · GitHub

Will land in 8.10 :slight_smile:

Hi Jonathan,

This is consumer-driver contract and the client needs to know per variable what type it is. This repeats the API idea from Typed Variables from Camunda 7. This approach solves the problem for trivial cases of individual variable access, but in general it would be very nice if the response is given back as JSON instead of a list of individual Variable instances. If it is a JSON, I could easily apply JSON → Type on my own using a JSON Mapper and convert it to a Map<String, Object> or a custom type.

My workaround now is to construct the JSON back and then parse it:

fun Map<String, String>.asJson(): String {
  val jsonParts = this.entries.joinToString(",") { (key, value) ->
    "\"$key\":$value"
  }
  return "{$jsonParts}"
}

fun String.parseJson(objectMapper: ObjectMapper): Map<String, Any?> =
  objectMapper.readValue<Map<String, Any?>>(this)

And in my retrieval code I’m invoking:

val result = variableRequest.withFullValues().send().join().items()

val variablesFromTask = result.associate { variable ->
                variable.name to variable.value
}.asJson().parseJson(jacksonObjectMapper())

Cheers,

Simon

Hello @zambrovski ,

as the endpoint to retrieve task variables works like a search endpoint, it returns a list of results.

However, I see the idea behind your approach.

Have you tried using a user task listener for this? Here, you would receive the variables of the user task in the job worker format.

I was thinking about it, but I see two limitations:

  • User Tasks Listeners are not supporting variables at all (the Execution Listeners do)
  • The invocation is done once coupled to the user task lifecycle and will require external storage of the variable values outside of the cluster.

Or am I missing something here?

Cheers,

Simon

Hello @zambrovski ,

a task listener does provide the variables, it just cannot update them via job completion.

The other part is more relevant. If this is a query functionality, I would actually also not use a listener for this.

Instead, I would also go for parsing the variable values. I see that this is some overhead, let me see what we can do about it.

The payload provided by the process should be retrievable. In general you handle it in whole as a JSON payload object and having the ability to apply “key”-filtering on the top level of this object is fine, but then I would still expect the JSON response to be one valid JSON object. So having:

{
  "name": "value",
  "name2": 17,
  "name3": true
}

and requesting variables name, name3 would result in:

{
  "name": "value",
  "name3": true
}

As an alternative multiple JSON Objects as list:

[
  { "name": "value"},
  { "name3": true }
]

Or even as a list of those in Java:

{ "name": "value"}

and

{ "name3": true }

Because from all those I could use the JSON types (null, boolean, number, string, array, object) and not their toString() representation sent to me in a string value.

Hello @zambrovski ,

there was a reason to do the same again (delivering variables for user tasks serialized as string). Not sure what it was (probably the feature to cut off a long variable which break the json format) but I agree, it makes the api uncomfortable.

We can compensate for this in the client.

I like your presented option 1 as it is exactly the format a job also gets the variables.