Skip to content

[Java][jersey2/jersey3] Fix serialization of array path parameters - #24986

Merged
wing328 merged 2 commits into
OpenAPITools:masterfrom
MatteoMolinari93:fix/jersey-array-path-params
Sep 23, 2026
Merged

wing328 merged 2 commits into
OpenAPITools:masterfrom
MatteoMolinari93:fix/jersey-array-path-params

Conversation

@MatteoMolinari93

@MatteoMolinari93 MatteoMolinari93 commented Sep 22, 2026 •

Copy link
Copy Markdown
Contributor

Closes #24985

Array path parameters were serialized with Collection.toString(), producing "[a, b, c]" in the URL. Add ApiClient.collectionPathParameterToString, which escapes each item individually and joins them with the delimiter for the parameter's collection format (csv by default).

PR checklist

  • Read the contribution guidelines.
  • Run the following to build the project and update samples:
    ./mvnw clean package || exit
    ./bin/generate-samples.sh ./bin/configs/*.yaml || exit
    ./bin/utils/export_docs_generators.sh || exit
    
    (For Windows users, please run the script in WSL)
    Commit all changed files.
    This is important, as CI jobs will verify all generator outputs of your HEAD commit as it would merge with master.
    These must match the expectations made by your contribution.
    You may regenerate an individual generator by passing the relevant config(s) as an argument to the script, for example ./bin/generate-samples.sh bin/configs/java*.
    IMPORTANT: Do NOT purge/delete any folders/files (e.g. tests) when regenerating the samples as manually written tests may be removed.
  • If your PR is targeting a particular programming language, @mention the technical committee members, so they are more likely to review the pull request.

@bbdouglas @sreeshas @jfiala @lukoyanov @cbornet @jeff9finger @karismann @Zomzog @lwlee2608 @martin-mfg @KannaKim


Summary by cubic

Fixes Java jersey2/jersey3 clients serializing array path parameters as [a, b, c] (from Collection.toString()) instead of the collection format's delimiter. Adds collectionPathParameterToString to ApiClient, which escapes each item individually and joins them with the correct delimiter; multi falls back to csv since it isn't valid for path parameters.

  • Preserves the delimiter before leading empty or null items (e.g. ["", "b"] becomes ",b").
  • Updates generated samples and adds runtime tests for collectionPathParameterToString plus a codegen test using a new array path parameter spec.

Written for commit ffbd441. Summary will update on new commits.

Review in cubic

Array path parameters were serialized with Collection.toString(), producing
"[a, b, c]" in the URL. Add ApiClient.collectionPathParameterToString, which
escapes each item individually and joins them with the delimiter for the
parameter's collection format (csv by default).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

@cubic-dev-ai cubic-dev-ai Bot 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.

3 issues found across 18 files

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="modules/openapi-generator/src/test/java/org/openapitools/codegen/java/JavaClientCodegenTest.java">

<violation number="1" location="modules/openapi-generator/src/test/java/org/openapitools/codegen/java/JavaClientCodegenTest.java:5371">
P3: The test proves the template invokes `collectionPathParameterToString("csv", ids)` and that the method exists, but never asserts the method's actual escaping behavior, which is the entire point of the PR. A behavioral check such as `csv` with an item containing `,` producing `a%2Cb,c` (item comma encoded, separator comma raw) would lock in per-item escaping and guard against regression to the old whole-string escape. Compare okhttp-gson's analogous helper (libraries/okhttp-gson/ApiClient.mustache), whose full-string escaping later breaks items containing the delimiter — exactly what this PR fixes for jersey and worth pinning with a test.</violation>
</file>

<file name="modules/openapi-generator/src/main/resources/Java/libraries/jersey2/ApiClient.mustache">

<violation number="1" location="modules/openapi-generator/src/main/resources/Java/libraries/jersey2/ApiClient.mustache:990">
P3: An empty (non-null) required array path parameter serializes to an empty string, so `{ids}` is replaced with an empty segment and the path becomes `/items//action`. The generated Api method only null-checks required params, so an empty list reaches this method. Compare the old behavior (`escapeString("[]")` → `%5B%5D`), which could not match any route, whereas an empty segment is liable to slash-collapsing/normalization by servers and can change route resolution. Return a placeholder or document this degenerate case explicitly.</violation>
</file>

<file name="samples/client/petstore/java/jersey3/src/main/java/org/openapitools/client/ApiClient.java">

<violation number="1" location="samples/client/petstore/java/jersey3/src/main/java/org/openapitools/client/ApiClient.java:988">
P3: This new method is not invoked anywhere in the 12 regenerated jersey samples: no generated API class in any jersey2/jersey3 sample calls `collectionPathParameterToString` (the only call site in the repo is `samples/client/others/java/resttemplate-list-schema-validation/.../UserApi.java:73`, which uses the resttemplate variant). None of the changed samples' specs define an array path parameter, so the serialization fix is never exercised at sample level and regressions in this escaping logic would not be caught by sample regeneration. The generator unit test (`JavaClientCodegenTest`) only verifies the generation output, not the runtime URL. Consider adding an array path parameter to one jersey2/jersey3 sample spec (or a runtime test on the method's output, e.g. csv `["a,b","c"]` → `a%2Cb,c`) so the fix is covered by samples.</violation>
</file>

Reply with feedback, questions, or to request a fix.

Re-trigger cubic

.contains("apiClient.collectionPathParameterToString(\"csv\", ids)")
.contains("apiClient.escapeString(id.toString())")
.doesNotContain("apiClient.escapeString(ids.toString())");
JavaFileAssert.assertThat(output.resolve("src/main/java/xyz/abcdef/invoker/ApiClient.java").toFile())

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.

P3: The test proves the template invokes collectionPathParameterToString("csv", ids) and that the method exists, but never asserts the method's actual escaping behavior, which is the entire point of the PR. A behavioral check such as csv with an item containing , producing a%2Cb,c (item comma encoded, separator comma raw) would lock in per-item escaping and guard against regression to the old whole-string escape. Compare okhttp-gson's analogous helper (libraries/okhttp-gson/ApiClient.mustache), whose full-string escaping later breaks items containing the delimiter — exactly what this PR fixes for jersey and worth pinning with a test.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At modules/openapi-generator/src/test/java/org/openapitools/codegen/java/JavaClientCodegenTest.java, line 5371:

<comment>The test proves the template invokes `collectionPathParameterToString("csv", ids)` and that the method exists, but never asserts the method's actual escaping behavior, which is the entire point of the PR. A behavioral check such as `csv` with an item containing `,` producing `a%2Cb,c` (item comma encoded, separator comma raw) would lock in per-item escaping and guard against regression to the old whole-string escape. Compare okhttp-gson's analogous helper (libraries/okhttp-gson/ApiClient.mustache), whose full-string escaping later breaks items containing the delimiter — exactly what this PR fixes for jersey and worth pinning with a test.</comment>

<file context>
@@ -5351,6 +5351,27 @@ public void testInsecureTlsHookOmittedWhenDisabled(String library) {
+                .contains("apiClient.collectionPathParameterToString(\"csv\", ids)")
+                .contains("apiClient.escapeString(id.toString())")
+                .doesNotContain("apiClient.escapeString(ids.toString())");
+        JavaFileAssert.assertThat(output.resolve("src/main/java/xyz/abcdef/invoker/ApiClient.java").toFile())
+                .assertMethod("collectionPathParameterToString", "String", "Collection<?>");
+    }
</file context>

*/
public String collectionPathParameterToString(String collectionFormat, Collection<?> value) {
if (value == null || value.isEmpty()) {
return "";

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.

P3: An empty (non-null) required array path parameter serializes to an empty string, so {ids} is replaced with an empty segment and the path becomes /items//action. The generated Api method only null-checks required params, so an empty list reaches this method. Compare the old behavior (escapeString("[]") → %5B%5D), which could not match any route, whereas an empty segment is liable to slash-collapsing/normalization by servers and can change route resolution. Return a placeholder or document this degenerate case explicitly.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At modules/openapi-generator/src/main/resources/Java/libraries/jersey2/ApiClient.mustache, line 990:

<comment>An empty (non-null) required array path parameter serializes to an empty string, so `{ids}` is replaced with an empty segment and the path becomes `/items//action`. The generated Api method only null-checks required params, so an empty list reaches this method. Compare the old behavior (`escapeString("[]")` → `%5B%5D`), which could not match any route, whereas an empty segment is liable to slash-collapsing/normalization by servers and can change route resolution. Return a placeholder or document this degenerate case explicitly.</comment>

<file context>
@@ -977,6 +977,39 @@ public class ApiClient{{#jsr310}} extends JavaTimeFormatter{{/jsr310}} {
+   */
+  public String collectionPathParameterToString(String collectionFormat, Collection<?> value) {
+    if (value == null || value.isEmpty()) {
+      return "";
+    }
+
</file context>

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Returning an empty string is intentional: for style: simple, RFC 6570 expands an empty list to an empty string, and the collectionPathParameterToString helpers in the resttemplate, webclient and restclient libraries behave the same way. The previous %5B%5D output was also an invalid path. A placeholder value would send a request the spec never defined, so I've kept this consistent with the other Java libraries.

* @param value Collection value
* @return Escaped path parameter value
*/
public String collectionPathParameterToString(String collectionFormat, Collection<?> value) {

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.

P3: This new method is not invoked anywhere in the 12 regenerated jersey samples: no generated API class in any jersey2/jersey3 sample calls collectionPathParameterToString (the only call site in the repo is samples/client/others/java/resttemplate-list-schema-validation/.../UserApi.java:73, which uses the resttemplate variant). None of the changed samples' specs define an array path parameter, so the serialization fix is never exercised at sample level and regressions in this escaping logic would not be caught by sample regeneration. The generator unit test (JavaClientCodegenTest) only verifies the generation output, not the runtime URL. Consider adding an array path parameter to one jersey2/jersey3 sample spec (or a runtime test on the method's output, e.g. csv ["a,b","c"] → a%2Cb,c) so the fix is covered by samples.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At samples/client/petstore/java/jersey3/src/main/java/org/openapitools/client/ApiClient.java, line 988:

<comment>This new method is not invoked anywhere in the 12 regenerated jersey samples: no generated API class in any jersey2/jersey3 sample calls `collectionPathParameterToString` (the only call site in the repo is `samples/client/others/java/resttemplate-list-schema-validation/.../UserApi.java:73`, which uses the resttemplate variant). None of the changed samples' specs define an array path parameter, so the serialization fix is never exercised at sample level and regressions in this escaping logic would not be caught by sample regeneration. The generator unit test (`JavaClientCodegenTest`) only verifies the generation output, not the runtime URL. Consider adding an array path parameter to one jersey2/jersey3 sample spec (or a runtime test on the method's output, e.g. csv `["a,b","c"]` → `a%2Cb,c`) so the fix is covered by samples.</comment>

<file context>
@@ -977,6 +977,39 @@ public String escapeString(String str) {
+   * @param value Collection value
+   * @return Escaped path parameter value
+   */
+  public String collectionPathParameterToString(String collectionFormat, Collection<?> value) {
+    if (value == null || value.isEmpty()) {
+      return "";
</file context>

- Keep the delimiter before leading empty or null items in
  collectionPathParameterToString (e.g. ["", "b"] -> ",b")
- Remove the unused pipedIds query param from the test spec
- Add runtime tests for collectionPathParameterToString to the
  jersey2-java8 and jersey3 sample ApiClientTest

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@wing328 wing328 added this to the 7.26.0 milestone Sep 23, 2026
@wing328
wing328 merged commit 76e3258 into OpenAPITools:master Sep 23, 2026
145 of 146 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[BUG][JAVA] jersey2/jersey3: array path parameters serialized as "[a, b]" instead of "a,b"

2 participants