Repository navigation
[Java][jersey2/jersey3] Fix serialization of array path parameters - #24986
Conversation
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>
There was a problem hiding this comment.
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()) |
There was a problem hiding this comment.
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 ""; |
There was a problem hiding this comment.
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>
There was a problem hiding this comment.
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) { |
There was a problem hiding this comment.
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>
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
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.
@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](fromCollection.toString()) instead of the collection format's delimiter. AddscollectionPathParameterToStringtoApiClient, which escapes each item individually and joins them with the correct delimiter;multifalls back to csv since it isn't valid for path parameters.["", "b"]becomes",b").collectionPathParameterToStringplus a codegen test using a new array path parameter spec.Written for commit ffbd441. Summary will update on new commits.