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>.
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:
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())
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.
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.