Skip to content

Commit 4c09a1c

Browse files
fix(java): serialize JSON header parameter content
1 parent b02d00b commit 4c09a1c

9 files changed

Lines changed: 169 additions & 9 deletions

File tree

‎modules/openapi-generator/src/main/java/org/openapitools/codegen/CodegenParameter.java‎

Lines changed: 16 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,10 @@ public class CodegenParameter implements IJsonSchemaValidationProperties {
5050
* If a query parameter should be serialized as json
5151
*/
5252
public boolean queryIsJsonMimeType;
53+
/**
54+
* True when a header parameter uses a JSON media type in Parameter.content.
55+
*/
56+
public boolean headerIsJsonMimeType;
5357
/**
5458
* datatype is the generic inner parameter of a std::optional for C++, or Optional (Java)
5559
*/
@@ -270,6 +274,7 @@ public CodegenParameter copy() {
270274
output.isArray = this.isArray;
271275
output.isMap = this.isMap;
272276
output.queryIsJsonMimeType = this.queryIsJsonMimeType;
277+
output.headerIsJsonMimeType = this.headerIsJsonMimeType;
273278
output.isOptional = this.isOptional;
274279
output.isExplode = this.isExplode;
275280
output.style = this.style;
@@ -294,15 +299,15 @@ public int hashCode() {
294299
isFormStyle, isSpaceDelimited, isPipeDelimited,
295300
jsonSchema, isString, isNumeric, isInteger, isLong, isNumber, isFloat, isDouble, isDecimal,
296301
isByteArray, isBinary, isBoolean, isDate, isDateTime, isUuid, isUri, isEmail, isPassword,
297-
isFreeFormObject, isAnyType, isArray, isMap, queryIsJsonMimeType, isOptional, isFile, isEnum, isEnumRef, _enum, allowableValues,
298-
items, mostInnerItems, additionalProperties, vars, requiredVars, vendorExtensions, hasValidation,
299-
getMaxProperties(), getMinProperties(), isNullable, isDeprecated, required, getMaximum(),
300-
getExclusiveMaximum(), getMinimum(), getExclusiveMinimum(), getMaxLength(), getMinLength(),
301-
getPattern(), getMaxItems(), getMinItems(), getUniqueItems(), contentType, multipleOf, isNull, isVoid,
302-
additionalPropertiesIsAnyType, hasVars, hasRequired, isShort, isUnboundedInteger,
303-
hasDiscriminatorWithNonEmptyMapping, composedSchemas, hasMultipleTypes, schema, content,
304-
requiredVarsMap, ref, uniqueItemsBoolean, schemaIsFromAdditionalProperties,
305-
nameInPascalCase, nameInCamelCase, nameInLowerCase, nameInSnakeCase);
302+
isFreeFormObject, isAnyType, isArray, isMap, queryIsJsonMimeType, headerIsJsonMimeType, isOptional,
303+
isFile, isEnum, isEnumRef, _enum, allowableValues, items, mostInnerItems, additionalProperties, vars,
304+
requiredVars, vendorExtensions, hasValidation, getMaxProperties(), getMinProperties(), isNullable,
305+
isDeprecated, required, getMaximum(), getExclusiveMaximum(), getMinimum(), getExclusiveMinimum(),
306+
getMaxLength(), getMinLength(), getPattern(), getMaxItems(), getMinItems(), getUniqueItems(),
307+
contentType, multipleOf, isNull, isVoid, additionalPropertiesIsAnyType, hasVars, hasRequired, isShort,
308+
isUnboundedInteger, hasDiscriminatorWithNonEmptyMapping, composedSchemas, hasMultipleTypes, schema,
309+
content, requiredVarsMap, ref, uniqueItemsBoolean, schemaIsFromAdditionalProperties, nameInPascalCase,
310+
nameInCamelCase, nameInLowerCase, nameInSnakeCase);
306311
}
307312

308313
@Override
@@ -345,6 +350,7 @@ public boolean equals(Object o) {
345350
isArray == that.isArray &&
346351
isMap == that.isMap &&
347352
queryIsJsonMimeType == that.queryIsJsonMimeType &&
353+
headerIsJsonMimeType == that.headerIsJsonMimeType &&
348354
isOptional == that.isOptional &&
349355
isFile == that.isFile &&
350356
isEnum == that.isEnum &&
@@ -486,6 +492,7 @@ public String toString() {
486492
sb.append(", isArray=").append(isArray);
487493
sb.append(", isMap=").append(isMap);
488494
sb.append(", queryIsJsonMimeType=").append(queryIsJsonMimeType);
495+
sb.append(", headerIsJsonMimeType=").append(headerIsJsonMimeType);
489496
sb.append(", isOptional=").append(isOptional);
490497
sb.append(", isFile=").append(isFile);
491498
sb.append(", isEnum=").append(isEnum);

‎modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultCodegen.java‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5722,6 +5722,7 @@ public CodegenParameter fromParameter(Parameter parameter, Set<String> imports)
57225722
codegenParameter.isPathParam = true;
57235723
} else if (parameter instanceof HeaderParameter || "header".equalsIgnoreCase(parameter.getIn())) {
57245724
codegenParameter.isHeaderParam = true;
5725+
codegenParameter.headerIsJsonMimeType = isJsonMimeType(codegenParameter.contentType);
57255726
} else if (parameter instanceof CookieParameter || "cookie".equalsIgnoreCase(parameter.getIn())) {
57265727
codegenParameter.isCookieParam = true;
57275728
} else {

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

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -912,6 +912,32 @@ 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+
String json = JSON.serialize(param);
923+
StringBuilder escaped = new StringBuilder(json.length());
924+
925+
for (int i = 0; i < json.length(); i++) {
926+
char c = json.charAt(i);
927+
if (c >= 0x7f) {
928+
escaped.append("\\u");
929+
escaped.append(Character.forDigit((c >> 12) & 0xf, 16));
930+
escaped.append(Character.forDigit((c >> 8) & 0xf, 16));
931+
escaped.append(Character.forDigit((c >> 4) & 0xf, 16));
932+
escaped.append(Character.forDigit(c & 0xf, 16));
933+
} else {
934+
escaped.append(c);
935+
}
936+
}
937+
938+
return escaped.toString();
939+
}
940+
915941
/**
916942
* Formats the specified query parameter to a list containing a single {@code Pair} object.
917943
*

‎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: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5362,6 +5362,18 @@ 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(1);
5374+
assertTrue(codegenOperation.headerParams.get(0).headerIsJsonMimeType);
5375+
}
5376+
53655377
@Test
53665378
public void testDefaultOauthIsNotNull() {
53675379
final DefaultCodegen codegen = new DefaultCodegen();

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

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -588,6 +588,33 @@ 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+
591618
@Test
592619
public void testGeneratePingSomeObj() {
593620
final Path output = newTempFolder();
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
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+
responses:
20+
'204':
21+
description: OK
22+
23+
components:
24+
schemas:
25+
HeaderArg:
26+
type: object
27+
required:
28+
- path
29+
properties:
30+
path:
31+
type: string
32+

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

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -867,6 +867,32 @@ public String parameterToString(Object param) {
867867
}
868868
}
869869

870+
/**
871+
* Format the given parameter object as an ASCII-safe JSON string.
872+
*
873+
* @param param Parameter
874+
* @return JSON representation of the parameter
875+
*/
876+
public String parameterToJsonString(Object param) {
877+
String json = JSON.serialize(param);
878+
StringBuilder escaped = new StringBuilder(json.length());
879+
880+
for (int i = 0; i < json.length(); i++) {
881+
char c = json.charAt(i);
882+
if (c >= 0x7f) {
883+
escaped.append("\\u");
884+
escaped.append(Character.forDigit((c >> 12) & 0xf, 16));
885+
escaped.append(Character.forDigit((c >> 8) & 0xf, 16));
886+
escaped.append(Character.forDigit((c >> 4) & 0xf, 16));
887+
escaped.append(Character.forDigit(c & 0xf, 16));
888+
} else {
889+
escaped.append(c);
890+
}
891+
}
892+
893+
return escaped.toString();
894+
}
895+
870896
/**
871897
* Formats the specified query parameter to a list containing a single {@code Pair} object.
872898
*

‎samples/client/petstore/java/okhttp-gson/src/test/java/org/openapitools/client/ApiClientTest.java‎

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -317,6 +317,30 @@ public void testParameterToPairsWhenValueIsCollection() throws Exception {
317317
}
318318
}
319319

320+
@Test
321+
public void testParameterToJsonStringAscii() {
322+
Map<String, String> value = new HashMap<>();
323+
value.put("path", "/x");
324+
325+
assertEquals("{\"path\":\"/x\"}", apiClient.parameterToJsonString(value));
326+
}
327+
328+
@Test
329+
public void testParameterToJsonStringEscapesNonAscii() {
330+
Map<String, String> value = new HashMap<>();
331+
value.put("path", "/тест");
332+
333+
assertEquals("{\"path\":\"/\\u0442\\u0435\\u0441\\u0442\"}", apiClient.parameterToJsonString(value));
334+
}
335+
336+
@Test
337+
public void testParameterToJsonStringEscapesDel() {
338+
Map<String, String> value = new HashMap<>();
339+
value.put("path", "/\u007f");
340+
341+
assertEquals("{\"path\":\"/\\u007f\"}", apiClient.parameterToJsonString(value));
342+
}
343+
320344
@Test
321345
public void testSanitizeFilename() {
322346
assertEquals("sun", apiClient.sanitizeFilename("sun"));

0 commit comments

Comments
 (0)