Skip to content

CAMEL-24230: Fix jsonpath writeAsString for object expressions - #24995

Merged
davsclaus merged 2 commits into
apache:mainfrom
atiaomar1978-hub:CAMEL-24230-jsonpath-writeAsString-map-fix
Jul 22, 2026
Merged

CAMEL-24230: Fix jsonpath writeAsString for object expressions#24995
davsclaus merged 2 commits into
apache:mainfrom
atiaomar1978-hub:CAMEL-24230-jsonpath-writeAsString-map-fix

Conversation

@atiaomar1978-hub

@atiaomar1978-hub atiaomar1978-hub commented Jul 21, 2026

Copy link
Copy Markdown
Contributor

Summary

Fixes CAMEL-24230: with writeAsString=true, a JsonPath expression that evaluates to a single JSON object (e.g. $.args) now returns a valid JSON String instead of a java.util.Map whose toString() looks like {age=30, name="Alice"}.

Root cause: JsonPathEngine had a special Map branch that only stringified individual values and returned the Map itself. Non-iterable results (including Maps) now use adapter.writeAsString(answer, exchange) like scalars already did.

Regression fix: JsonPathSplitWriteAsStringMapTest updated to split on $.content.* (array of object values) instead of relying on the old Map-entry behaviour.

Upgrade guide: Added a camel-jsonpath entry to camel-4x-upgrade-guide-4_22.adoc documenting the behavioral change and migration path for split routes that relied on the old Map behaviour.

Test plan

  • JsonPathWriteAsStringObjectTest (4) — route-level setHeader/setBody with jsonpathWriteAsString("$.args"), nested object, valid JSON output (AssertJ + JsonNode comparison)
  • JsonPathEngineWriteAsStringTest (3) — engine unit tests: object → JSON string, flag off → Map, array → List of JSON strings
  • Updated JsonPathSplitWriteAsStringMapTest — split on $.content.* expects String bodies
  • Full module: mvn test -pl components/camel-jsonpath119 tests passed
  • Code formatted (formatter:format, impsort:sort)

Generated by Cursor Agent on behalf of @atiaomar1978-hub.

When writeAsString=true and a JsonPath expression evaluates to a single JSON
object (Map), JsonPathEngine now serializes the entire object to a valid JSON
string instead of returning a Map with stringified values.

Adds AssertJ-based route and engine unit tests covering the reported $.args
scenario, nested objects, array elements, and the split-on-map regression path.

Co-authored-by: Cursor Agent <cursoragent@cursor.com>

@gnodet gnodet left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Claude Code on behalf of gnodet

Review Summary

Clean, well-targeted fix for CAMEL-24230. The production change removes a 12-line special-case branch that was the root cause, letting Map results flow through the existing scalar serialization path. Good test coverage across three test classes.

What I Love

  1. Minimal, surgical production change -- rather than adding complexity, the fix removes the problematic Map branch entirely, letting Maps fall through to the existing writeAsString path that already handles scalars correctly. Less code, fewer branches, correct behavior. The history backs this up: the Map branch was introduced in CAMEL-11558 (2017) as a workaround because the split EIP couldn't handle Maps at the time. CAMEL-17101 (2021) taught the split EIP to handle Maps properly, making the workaround obsolete -- but the branch stayed, creating this inconsistency.

  2. Thorough test strategy -- the PR includes both unit-level tests (JsonPathEngineWriteAsStringTest testing the engine directly) and integration-level tests (JsonPathWriteAsStringObjectTest testing via Camel routes with setHeader/setBody). The writeAsStringDoesNotReturnMapToString test with .doesNotContain("=") is a particularly good regression guard -- it catches exactly the symptom from the bug report ({age=30, name=Alice} instead of valid JSON).

  3. Regression-aware test update -- updating JsonPathSplitWriteAsStringMapTest to use $.content.* instead of $.content shows understanding of the full impact. The wildcard expression returns a List (handled by the Iterable branch), which is the correct way to get splittable JSON strings from a Map-shaped document.

Findings

Critical

None -- nice work!

Important

[Important] Consider an upgrade guide entry for the behavioral change. Per project conventions, user-visible changes should be documented in the upgrade guide. This fix changes observable behavior: previously, writeAsString=true with a Map-typed JsonPath result returned a Map with individually stringified values; now it returns a JSON String of the entire object.

Users who relied on the old behavior with .split().jsonpathWriteAsString("$.expression-returning-map") will need to change their expression to use a wildcard (e.g., $.content.* instead of $.content) to get a splittable list of JSON strings. A brief note in docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_22.adoc would help:

=== camel-jsonpath

The `writeAsString` option now correctly serializes single JSON object results (Maps) to a JSON String.
Previously, a JsonPath expression evaluating to a JSON object (e.g. `$.args`) with `writeAsString=true`
would return a `java.util.Map` with individually stringified values instead of a valid JSON String.
If you were relying on the old behavior to split a Map result, change the expression to use a wildcard
(e.g. `$.content.*` instead of `$.content`) to get a splittable list of JSON strings.

Suggestions & Nits

None -- the code is clean, well-formatted, and follows project conventions. The assertj-core test dependency is managed in the parent BOM, and the new tests use tree-based JSON comparison (ObjectMapper.readTree) where key order could vary, which is the right approach.

Overall

Solid contribution that fixes a real user-facing inconsistency in the writeAsString contract. The approach of removing incorrect code rather than adding workarounds is exactly right. Thank you for the thorough tests, @atiaomar1978-hub!

…de guide

Add a camel-jsonpath entry to the 4.22 upgrade guide explaining that writeAsString
now serializes JSON object results to a valid JSON String, and how to migrate split
routes that relied on the previous Map-entry behavior.

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
@atiaomar1978-hub

Copy link
Copy Markdown
Contributor Author

Thanks @gnodet for the review and approval.

Addressed feedback

[Important] Upgrade guide entry — done in commit 581501b.

Added a === camel-jsonpath section to docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_22.adoc documenting:

  • writeAsString now correctly serializes single JSON object results (Maps) to a valid JSON String
  • The previous behavior returned a java.util.Map with individually stringified values (the CAMEL-24230 bug)
  • Migration note for routes that split a Map result: use a wildcard expression (e.g. $.content.* instead of $.content) to obtain a splittable list of JSON strings

The PR description has also been updated to reflect this addition.

No further code changes were required beyond the original fix and tests.


Cursor Agent on behalf of @atiaomar1978-hub

@github-actions

Copy link
Copy Markdown
Contributor

🌟 Thank you for your contribution to the Apache Camel project! 🌟
🤖 CI automation will test this PR automatically.

🐫 Apache Camel Committers, please review the following items:

  • First-time contributors require MANUAL approval for the GitHub Actions to run
  • You can use the command /component-test (camel-)component-name1 (camel-)component-name2.. to request a test from the test bot although they are normally detected and executed by CI.
  • You can label PRs using skip-tests and test-dependents to fine-tune the checks executed by this PR.
  • Build and test logs are available in the summary page. Only Apache Camel committers have access to the summary.

⚠️ Be careful when sharing logs. Review their contents before sharing them publicly.

@davsclaus davsclaus added this to the 4.22.0 milestone Jul 22, 2026
@davsclaus davsclaus added the bug Something isn't working label Jul 22, 2026
@davsclaus
davsclaus merged commit 5aa6b98 into apache:main Jul 22, 2026
6 checks passed
@github-actions

Copy link
Copy Markdown
Contributor

🧪 CI tested the following changed modules:

  • components/camel-jsonpath
  • docs

🔬 Scalpel shadow comparison — Scalpel: 10 tested, 28 compile-only — current: 9 all tested

Maveniverse Scalpel detected 38 affected modules (current approach: 9).

⚠️ Modules only in Scalpel (29)
  • apache-camel
  • camel-allcomponents
  • camel-catalog
  • camel-catalog-console
  • camel-catalog-lucene
  • camel-catalog-maven
  • camel-catalog-suggest
  • camel-componentdsl
  • camel-csimple-maven-plugin
  • camel-endpointdsl
  • camel-endpointdsl-support
  • camel-itest
  • camel-jbang-core
  • camel-jbang-it
  • camel-jbang-main
  • camel-jbang-plugin-edit
  • camel-jbang-plugin-generate
  • camel-jbang-plugin-kubernetes
  • camel-jbang-plugin-test
  • camel-kamelet-main
  • camel-launcher
  • camel-report-maven-plugin
  • camel-route-parser
  • camel-yaml-dsl
  • camel-yaml-dsl-deserializers
  • camel-yaml-dsl-maven-plugin
  • coverage
  • docs
  • dummy-component

Skip-tests mode would test 10 modules (2 direct + 8 downstream), skip tests for 28 (generated code, meta-modules)

Modules Scalpel would test (10)
  • camel-jbang-mcp
  • camel-jbang-plugin-mcp
  • camel-jbang-plugin-route-parser
  • camel-jbang-plugin-tui
  • camel-jbang-plugin-validate
  • camel-jsonpath
  • camel-launcher-container
  • camel-yaml-dsl-validator
  • camel-yaml-dsl-validator-maven-plugin
  • docs
Modules with tests skipped (28)
  • apache-camel
  • camel-allcomponents
  • camel-catalog
  • camel-catalog-console
  • camel-catalog-lucene
  • camel-catalog-maven
  • camel-catalog-suggest
  • camel-componentdsl
  • camel-csimple-maven-plugin
  • camel-endpointdsl
  • camel-endpointdsl-support
  • camel-itest
  • camel-jbang-core
  • camel-jbang-it
  • camel-jbang-main
  • camel-jbang-plugin-edit
  • camel-jbang-plugin-generate
  • camel-jbang-plugin-kubernetes
  • camel-jbang-plugin-test
  • camel-kamelet-main
  • camel-launcher
  • camel-report-maven-plugin
  • camel-route-parser
  • camel-yaml-dsl
  • camel-yaml-dsl-deserializers
  • camel-yaml-dsl-maven-plugin
  • coverage
  • dummy-component

ℹ️ Shadow mode — Scalpel observes but does not affect test execution. Learn more

All tested modules (38 modules)
  • Camel :: All Components Sync point
  • Camel :: Assembly
  • Camel :: Catalog :: CSimple Maven Plugin (deprecated)
  • Camel :: Catalog :: Camel Catalog
  • Camel :: Catalog :: Camel Report Maven Plugin
  • Camel :: Catalog :: Camel Route Parser
  • Camel :: Catalog :: Console
  • Camel :: Catalog :: Dummy Component
  • Camel :: Catalog :: Lucene (deprecated)
  • Camel :: Catalog :: Maven
  • Camel :: Catalog :: Suggest
  • Camel :: Component DSL
  • Camel :: Coverage
  • Camel :: Docs
  • Camel :: Endpoint DSL
  • Camel :: Endpoint DSL :: Support
  • Camel :: Integration Tests
  • Camel :: JBang :: Core
  • Camel :: JBang :: Integration tests
  • Camel :: JBang :: MCP
  • Camel :: JBang :: Main
  • Camel :: JBang :: Plugin :: Edit
  • Camel :: JBang :: Plugin :: Generate
  • Camel :: JBang :: Plugin :: Kubernetes
  • Camel :: JBang :: Plugin :: MCP
  • Camel :: JBang :: Plugin :: Route Parser
  • Camel :: JBang :: Plugin :: TUI
  • Camel :: JBang :: Plugin :: Testing
  • Camel :: JBang :: Plugin :: Validate
  • Camel :: JSon Path
  • Camel :: Kamelet Main
  • Camel :: Launcher
  • Camel :: Launcher :: Container
  • Camel :: YAML DSL
  • Camel :: YAML DSL :: Deserializers
  • Camel :: YAML DSL :: Maven Plugins
  • Camel :: YAML DSL :: Validator
  • Camel :: YAML DSL :: Validator Maven Plugin

⚙️ View full build and test results

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working components docs

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants