Claus Ibsen created CAMEL-25334:
-----------------------------------
Summary: Content-type aware payloads for low-code: the body stays
text with its content type, Groovy and simple read JSON fields from it,
structured results go back to text
Key: CAMEL-25334
URL: https://issues.apache.org/jira/browse/CAMEL-25334
Project: Camel
Issue Type: New Feature
Components: camel-groovy, camel-core
Reporter: Claus Ibsen
Low-code users, and AI models writing routes, think of the payload as JSON (or
XML, CSV, text) and write data mappings in Groovy and simple accordingly. Camel
carries the native Java type of each component instead - a GenericFile, a
byte[], an InputStream, a Map - and the gap shows up as runtime errors:
* Groovy {{body.find { it.sku == headers.sku } }} on JSON that was not
unmarshalled: {{No such property: sku for class: java.lang.Byte}}
* simple {{$\{body[amount]\}}} on the file consumer's body: {{Key: amount not
found in bean: GenericFile[...]}}
* a mapping that returns a Map, sent to an http producer: no type converter
from LinkedHashMap to InputStream
* a jsonpath result stored in a header as a LinkedList, which disappears when
the message crosses a JMS broker
Raymond Meester described the same from the Fluxygen low-code platform on
Camel, where users write Groovy and simple without knowing Java:
[Text-orientated vs. Object-orientated in
integration|https://camel.zulipchat.com/#narrow/channel/257298-camel/topic/.E2.9C.94.20Text-orientated.20vs.2E.20Object-orientated.20in.20integration/with/568024059].
In the local-model benchmark, 19 of 156 attempts in one series died on such a
body type collision.
h3. Proposal: a content-type aware mode
Not "everything is text", and not a second Camel: the payload keeps its content
type, the body stays the raw text or bytes between steps, and each language
reads it the way it expects:
||who||what it sees when the content type is JSON||
|jsonpath, jq|the raw JSON text, as today|
|Groovy|parsed data (Map / List), so {{body.sku}}, {{body.find \{ it.sku == ...
\}}}, {{body.items.each \{ ... \}}} work; the text stays available (for example
as {{bodyText}})|
|simple {{$\{body[sku]\}}}, {{$\{body.sku\}}}|the field, read from the parsed
JSON|
|producers|the text, unchanged|
|marshal json|the text, passed through (CAMEL-25329)|
The return side matters as much as the read side: a structured result (Map,
List, JsonNode) that a language stores with setBody or setHeader becomes JSON
text again, so the next producer, broker or mapping gets text and headers
survive a JMS hop.
Where the content type comes from: the Content-Type header, which HTTP
consumers and Jackson's marshal already set; for the file consumer, the file
name through MimeTypeHelper (probeContentType); otherwise the first character
of the text. Binary content stays binary, and a size threshold (as stream
caching has) keeps large payloads as streams.
Groovy needs no new dependency: camel-groovy already depends on groovy-json and
groovy-xml (JsonSlurper gives Map/List, XmlSlurper GPath for XML). Simple
already compares text numbers numerically (ObjectHelper.typeCoerceCompare), so
{{$\{header.qty\} > 5}} keeps working with text headers.
h3. Prototype and measurement
# Groovy, behind an option: a JSON body is bound as Map/List, the text as
bodyText, a Map/List result goes back to JSON text.
# simple: {{$\{body[x]\}}} and {{$\{body.x\}}} read fields from JSON text.
# Headers: a structured result from any language becomes JSON text when the
mode is on.
# Run the local-model benchmark ladder with the mode off and on and compare the
body type collisions and pass rate before deciding on the switch (one
camel.main property, or per language) and on defaults.
Classic routes keep the native body types; the mode is opt-in.
Related: CAMEL-24844 (body type flow), CAMEL-25330 (the Groovy hint this would
replace with behaviour), CAMEL-25329 (marshal passes JSON text through).
_Claude Code on behalf of davsclaus_
--
This message was sent by Atlassian Jira
(v8.20.10#820010)