Skip to content

Commit 099d598

Browse files
fix(java): serialize JSON header parameter content (#25056)
* fix(java): serialize JSON header parameter content * fix(java): preserve null JSON header semantics
1 parent f48e736 commit 099d598

22 files changed

Lines changed: 838 additions & 2 deletions

File tree

‎modules/openapi-generator/src/main/resources/Java/libraries/okhttp-gson/ApiClient.mustache‎

Lines changed: 45 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -912,6 +912,46 @@ public class ApiClient {
912912
}
913913
}
914914
915+
/**
916+
* Format the given parameter object as an ASCII-safe JSON string.
917+
*
918+
* @param param Parameter
919+
* @return JSON representation of the parameter
920+
*/
921+
public String parameterToJsonString(Object param) {
922+
if (param == null) {
923+
return "";
924+
}
925+
String json = JSON.serialize(param);
926+
int firstUnsafe = -1;
927+
for (int i = 0; i < json.length(); i++) {
928+
if (json.charAt(i) >= 0x7f) {
929+
firstUnsafe = i;
930+
break;
931+
}
932+
}
933+
if (firstUnsafe == -1) {
934+
return json;
935+
}
936+
937+
StringBuilder escaped = new StringBuilder(json.length());
938+
escaped.append(json, 0, firstUnsafe);
939+
for (int i = firstUnsafe; i < json.length(); i++) {
940+
char c = json.charAt(i);
941+
if (c >= 0x7f) {
942+
escaped.append("\\u");
943+
escaped.append(Character.forDigit((c >> 12) & 0xf, 16));
944+
escaped.append(Character.forDigit((c >> 8) & 0xf, 16));
945+
escaped.append(Character.forDigit((c >> 4) & 0xf, 16));
946+
escaped.append(Character.forDigit(c & 0xf, 16));
947+
} else {
948+
escaped.append(c);
949+
}
950+
}
951+
952+
return escaped.toString();
953+
}
954+
915955
/**
916956
* Formats the specified query parameter to a list containing a single {@code Pair} object.
917957
*
@@ -1954,7 +1994,11 @@ public class ApiClient {
19541994
}
19551995
break;
19561996
case "header":
1957-
headerParams.put(param.getName(), parameterToString(value));
1997+
if (param.getContent() != null && param.getContent().containsKey("application/json")) {
1998+
headerParams.put(param.getName(), parameterToJsonString(value));
1999+
} else {
2000+
headerParams.put(param.getName(), parameterToString(value));
2001+
}
19582002
break;
19592003
case "cookie":
19602004
cookieParams.put(param.getName(), parameterToString(value));

‎modules/openapi-generator/src/main/resources/Java/libraries/okhttp-gson/api.mustache‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -229,7 +229,12 @@ public class {{classname}} {
229229
{{#headerParams}}
230230

231231
if ({{paramName}} != null) {
232+
{{#headerIsJsonMimeType}}
233+
localVarHeaderParams.put("{{baseName}}", localVarApiClient.parameterToJsonString({{paramName}}));
234+
{{/headerIsJsonMimeType}}
235+
{{^headerIsJsonMimeType}}
232236
localVarHeaderParams.put("{{baseName}}", localVarApiClient.parameterToString({{paramName}}));
237+
{{/headerIsJsonMimeType}}
233238
}
234239

235240
{{/headerParams}}

‎modules/openapi-generator/src/test/java/org/openapitools/codegen/DefaultCodegenTest.java‎

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5362,6 +5362,29 @@ public void testQueryIsJsonMimeType() {
53625362
assertTrue(codegenOperation.queryParams.stream().allMatch(p -> p.queryIsJsonMimeType));
53635363
}
53645364

5365+
@Test
5366+
public void testHeaderIsJsonMimeType() {
5367+
DefaultCodegen codegen = new DefaultCodegen();
5368+
final OpenAPI openAPI = TestUtils.parseFlattenSpec("src/test/resources/3_1/java/json-header-content.yaml");
5369+
codegen.setOpenAPI(openAPI);
5370+
String path = "/test";
5371+
CodegenOperation codegenOperation = codegen.fromOperation(path, "POST", openAPI.getPaths().get(path).getPost(), null);
5372+
5373+
assertThat(codegenOperation.headerParams).hasSize(2);
5374+
5375+
CodegenParameter jsonHeader = codegenOperation.headerParams.stream()
5376+
.filter(param -> "X-Json-Arg".equals(param.baseName))
5377+
.findFirst()
5378+
.orElseThrow();
5379+
assertThat(jsonHeader.headerIsJsonMimeType).isTrue();
5380+
5381+
CodegenParameter plainHeader = codegenOperation.headerParams.stream()
5382+
.filter(param -> "X-Plain-Arg".equals(param.baseName))
5383+
.findFirst()
5384+
.orElseThrow();
5385+
assertThat(plainHeader.headerIsJsonMimeType).isFalse();
5386+
}
5387+
53655388
@Test
53665389
public void testDefaultOauthIsNotNull() {
53675390
final DefaultCodegen codegen = new DefaultCodegen();

‎modules/openapi-generator/src/test/java/org/openapitools/codegen/java/JavaClientCodegenTest.java‎

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -588,6 +588,60 @@ public void testJersey3NullableNullDefaultRemainsUndefined() {
588588
.doesNotContain("JsonNullable.<List<String>>of(null)");
589589
}
590590

591+
@Test
592+
public void testJsonContentHeaderUsesJsonSerialization() {
593+
final Path output = newTempFolder();
594+
final CodegenConfigurator configurator = new CodegenConfigurator()
595+
.setGeneratorName(JAVA_GENERATOR)
596+
.setLibrary(JavaClientCodegen.OKHTTP_GSON)
597+
.setInputSpec("src/test/resources/3_1/java/json-header-content.yaml")
598+
.setOutputDir(output.toString().replace("\\", "/"));
599+
600+
List<File> files = new DefaultGenerator().opts(configurator.toClientOptInput()).generate();
601+
602+
validateJavaSourceFiles(files);
603+
604+
assertThat(output.resolve("src/main/java/org/openapitools/client/ApiClient.java"))
605+
.content()
606+
.contains("public String parameterToJsonString(Object param)");
607+
608+
assertThat(output.resolve("src/main/java/org/openapitools/client/api/DefaultApi.java"))
609+
.content()
610+
.contains(
611+
"localVarHeaderParams.put(\"X-Json-Arg\", localVarApiClient.parameterToJsonString(xJsonArg));"
612+
)
613+
.doesNotContain(
614+
"localVarHeaderParams.put(\"X-Json-Arg\", localVarApiClient.parameterToString(xJsonArg));"
615+
);
616+
}
617+
618+
@Test
619+
public void testDynamicJsonContentHeaderUsesJsonSerialization() {
620+
final Path output = newTempFolder();
621+
final CodegenConfigurator configurator = new CodegenConfigurator()
622+
.setGeneratorName(JAVA_GENERATOR)
623+
.setLibrary(JavaClientCodegen.OKHTTP_GSON)
624+
.setInputSpec("src/test/resources/3_1/java/json-header-content.yaml")
625+
.addAdditionalProperty("dynamicOperations", true)
626+
.setOutputDir(output.toString().replace("\\", "/"));
627+
628+
List<File> files = new DefaultGenerator().opts(configurator.toClientOptInput()).generate();
629+
630+
validateJavaSourceFiles(files);
631+
632+
assertThat(output.resolve("src/main/java/org/openapitools/client/ApiClient.java"))
633+
.content()
634+
.contains("if (param.getContent() != null && param.getContent().containsKey(\"application/json\")) {")
635+
.contains("public String parameterToJsonString(Object param) {\n if (param == null) {\n return \"\";\n }")
636+
.contains("headerParams.put(param.getName(), parameterToJsonString(value));")
637+
.contains("headerParams.put(param.getName(), parameterToString(value));");
638+
639+
assertThat(output.resolve("src/main/java/org/openapitools/client/api/DefaultApi.java"))
640+
.content()
641+
.contains("paramMap.put(\"X-Json-Arg\", xJsonArg);")
642+
.contains("paramMap.put(\"X-Plain-Arg\", xPlainArg);");
643+
}
644+
591645
@Test
592646
public void testGeneratePingSomeObj() {
593647
final Path output = newTempFolder();
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
openapi: 3.1.0
2+
3+
info:
4+
title: JSON header repro
5+
version: 1.0.0
6+
7+
paths:
8+
/test:
9+
post:
10+
operationId: testJsonHeader
11+
parameters:
12+
- name: X-Json-Arg
13+
in: header
14+
required: true
15+
content:
16+
application/json:
17+
schema:
18+
$ref: '#/components/schemas/HeaderArg'
19+
- name: X-Plain-Arg
20+
in: header
21+
schema:
22+
type: string
23+
responses:
24+
'204':
25+
description: OK
26+
27+
components:
28+
schemas:
29+
HeaderArg:
30+
type: object
31+
required:
32+
- path
33+
properties:
34+
path:
35+
type: string
36+

‎samples/client/echo_api/java/okhttp-gson-user-defined-templates/src/main/java/org/openapitools/client/ApiClient.java‎

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -793,6 +793,46 @@ public String parameterToString(Object param) {
793793
}
794794
}
795795

796+
/**
797+
* Format the given parameter object as an ASCII-safe JSON string.
798+
*
799+
* @param param Parameter
800+
* @return JSON representation of the parameter
801+
*/
802+
public String parameterToJsonString(Object param) {
803+
if (param == null) {
804+
return "";
805+
}
806+
String json = JSON.serialize(param);
807+
int firstUnsafe = -1;
808+
for (int i = 0; i < json.length(); i++) {
809+
if (json.charAt(i) >= 0x7f) {
810+
firstUnsafe = i;
811+
break;
812+
}
813+
}
814+
if (firstUnsafe == -1) {
815+
return json;
816+
}
817+
818+
StringBuilder escaped = new StringBuilder(json.length());
819+
escaped.append(json, 0, firstUnsafe);
820+
for (int i = firstUnsafe; i < json.length(); i++) {
821+
char c = json.charAt(i);
822+
if (c >= 0x7f) {
823+
escaped.append("\\u");
824+
escaped.append(Character.forDigit((c >> 12) & 0xf, 16));
825+
escaped.append(Character.forDigit((c >> 8) & 0xf, 16));
826+
escaped.append(Character.forDigit((c >> 4) & 0xf, 16));
827+
escaped.append(Character.forDigit(c & 0xf, 16));
828+
} else {
829+
escaped.append(c);
830+
}
831+
}
832+
833+
return escaped.toString();
834+
}
835+
796836
/**
797837
* Formats the specified query parameter to a list containing a single {@code Pair} object.
798838
*

‎samples/client/echo_api/java/okhttp-gson/src/main/java/org/openapitools/client/ApiClient.java‎

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -723,6 +723,46 @@ public String parameterToString(Object param) {
723723
}
724724
}
725725

726+
/**
727+
* Format the given parameter object as an ASCII-safe JSON string.
728+
*
729+
* @param param Parameter
730+
* @return JSON representation of the parameter
731+
*/
732+
public String parameterToJsonString(Object param) {
733+
if (param == null) {
734+
return "";
735+
}
736+
String json = JSON.serialize(param);
737+
int firstUnsafe = -1;
738+
for (int i = 0; i < json.length(); i++) {
739+
if (json.charAt(i) >= 0x7f) {
740+
firstUnsafe = i;
741+
break;
742+
}
743+
}
744+
if (firstUnsafe == -1) {
745+
return json;
746+
}
747+
748+
StringBuilder escaped = new StringBuilder(json.length());
749+
escaped.append(json, 0, firstUnsafe);
750+
for (int i = firstUnsafe; i < json.length(); i++) {
751+
char c = json.charAt(i);
752+
if (c >= 0x7f) {
753+
escaped.append("\\u");
754+
escaped.append(Character.forDigit((c >> 12) & 0xf, 16));
755+
escaped.append(Character.forDigit((c >> 8) & 0xf, 16));
756+
escaped.append(Character.forDigit((c >> 4) & 0xf, 16));
757+
escaped.append(Character.forDigit(c & 0xf, 16));
758+
} else {
759+
escaped.append(c);
760+
}
761+
}
762+
763+
return escaped.toString();
764+
}
765+
726766
/**
727767
* Formats the specified query parameter to a list containing a single {@code Pair} object.
728768
*

‎samples/client/others/java/okhttp-gson-oneOf-array/src/main/java/org/openapitools/client/ApiClient.java‎

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -697,6 +697,46 @@ public String parameterToString(Object param) {
697697
}
698698
}
699699

700+
/**
701+
* Format the given parameter object as an ASCII-safe JSON string.
702+
*
703+
* @param param Parameter
704+
* @return JSON representation of the parameter
705+
*/
706+
public String parameterToJsonString(Object param) {
707+
if (param == null) {
708+
return "";
709+
}
710+
String json = JSON.serialize(param);
711+
int firstUnsafe = -1;
712+
for (int i = 0; i < json.length(); i++) {
713+
if (json.charAt(i) >= 0x7f) {
714+
firstUnsafe = i;
715+
break;
716+
}
717+
}
718+
if (firstUnsafe == -1) {
719+
return json;
720+
}
721+
722+
StringBuilder escaped = new StringBuilder(json.length());
723+
escaped.append(json, 0, firstUnsafe);
724+
for (int i = firstUnsafe; i < json.length(); i++) {
725+
char c = json.charAt(i);
726+
if (c >= 0x7f) {
727+
escaped.append("\\u");
728+
escaped.append(Character.forDigit((c >> 12) & 0xf, 16));
729+
escaped.append(Character.forDigit((c >> 8) & 0xf, 16));
730+
escaped.append(Character.forDigit((c >> 4) & 0xf, 16));
731+
escaped.append(Character.forDigit(c & 0xf, 16));
732+
} else {
733+
escaped.append(c);
734+
}
735+
}
736+
737+
return escaped.toString();
738+
}
739+
700740
/**
701741
* Formats the specified query parameter to a list containing a single {@code Pair} object.
702742
*

‎samples/client/others/java/okhttp-gson-oneOf/src/main/java/org/openapitools/client/ApiClient.java‎

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -697,6 +697,46 @@ public String parameterToString(Object param) {
697697
}
698698
}
699699

700+
/**
701+
* Format the given parameter object as an ASCII-safe JSON string.
702+
*
703+
* @param param Parameter
704+
* @return JSON representation of the parameter
705+
*/
706+
public String parameterToJsonString(Object param) {
707+
if (param == null) {
708+
return "";
709+
}
710+
String json = JSON.serialize(param);
711+
int firstUnsafe = -1;
712+
for (int i = 0; i < json.length(); i++) {
713+
if (json.charAt(i) >= 0x7f) {
714+
firstUnsafe = i;
715+
break;
716+
}
717+
}
718+
if (firstUnsafe == -1) {
719+
return json;
720+
}
721+
722+
StringBuilder escaped = new StringBuilder(json.length());
723+
escaped.append(json, 0, firstUnsafe);
724+
for (int i = firstUnsafe; i < json.length(); i++) {
725+
char c = json.charAt(i);
726+
if (c >= 0x7f) {
727+
escaped.append("\\u");
728+
escaped.append(Character.forDigit((c >> 12) & 0xf, 16));
729+
escaped.append(Character.forDigit((c >> 8) & 0xf, 16));
730+
escaped.append(Character.forDigit((c >> 4) & 0xf, 16));
731+
escaped.append(Character.forDigit(c & 0xf, 16));
732+
} else {
733+
escaped.append(c);
734+
}
735+
}
736+
737+
return escaped.toString();
738+
}
739+
700740
/**
701741
* Formats the specified query parameter to a list containing a single {@code Pair} object.
702742
*

0 commit comments

Comments
 (0)