Skip to content

Commit bfb4629

Browse files
committed
Java client: decouple JSON handling
1 parent 112a7ec commit bfb4629

File tree

6 files changed

+124
-87
lines changed

6 files changed

+124
-87
lines changed

modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/JavaClientCodegen.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -116,9 +116,10 @@ public void processOpts() {
116116
supportingFiles.add(new SupportingFile("ApiClient.mustache", invokerFolder, "ApiClient.java"));
117117
supportingFiles.add(new SupportingFile("apiException.mustache", invokerFolder, "ApiException.java"));
118118
supportingFiles.add(new SupportingFile("Configuration.mustache", invokerFolder, "Configuration.java"));
119-
supportingFiles.add(new SupportingFile("JsonUtil.mustache", invokerFolder, "JsonUtil.java"));
120-
supportingFiles.add(new SupportingFile("StringUtil.mustache", invokerFolder, "StringUtil.java"));
119+
supportingFiles.add(new SupportingFile("JSON.mustache", invokerFolder, "JSON.java"));
121120
supportingFiles.add(new SupportingFile("Pair.mustache", invokerFolder, "Pair.java"));
121+
supportingFiles.add(new SupportingFile("StringUtil.mustache", invokerFolder, "StringUtil.java"));
122+
supportingFiles.add(new SupportingFile("TypeRef.mustache", invokerFolder, "TypeRef.java"));
122123

123124
final String authFolder = (sourceFolder + File.separator + invokerPackage + ".auth").replace(".", File.separator);
124125
supportingFiles.add(new SupportingFile("auth/Authentication.mustache", authFolder, "Authentication.java"));

modules/swagger-codegen/src/main/resources/Java/ApiClient.mustache

Lines changed: 37 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,7 @@
11
package {{invokerPackage}};
22

3-
import com.fasterxml.jackson.core.JsonGenerator.Feature;
43
import com.fasterxml.jackson.databind.*;
54
import com.fasterxml.jackson.annotation.*;
6-
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
75

86
import com.sun.jersey.api.client.Client;
97
import com.sun.jersey.api.client.ClientResponse;
@@ -48,6 +46,7 @@ public class ApiClient {
4846
private Map<String, String> defaultHeaderMap = new HashMap<String, String>();
4947
private boolean debugging = false;
5048
private String basePath = "{{basePath}}";
49+
private JSON json = new JSON();
5150
5251
private Map<String, Authentication> authentications;
5352
@@ -340,50 +339,38 @@ public class ApiClient {
340339
}
341340

342341
/**
343-
* Deserialize the given JSON string to Java object.
344-
*
345-
* @param json The JSON string
346-
* @param containerType The container type, one of "list", "array" or ""
347-
* @param cls The type of the Java object
348-
* @return The deserialized Java object
342+
* Serialize the given Java object into string according the given
343+
* Content-Type (only JSON is supported for now).
349344
*/
350-
public Object deserialize(String json, String containerType, Class cls) throws ApiException {
351-
if(null != containerType) {
352-
containerType = containerType.toLowerCase();
353-
}
354-
try{
355-
if("list".equals(containerType) || "array".equals(containerType)) {
356-
JavaType typeInfo = JsonUtil.getJsonMapper().getTypeFactory().constructCollectionType(List.class, cls);
357-
List response = (List<?>) JsonUtil.getJsonMapper().readValue(json, typeInfo);
358-
return response;
359-
}
360-
else if(String.class.equals(cls)) {
361-
if(json != null && json.startsWith("\"") && json.endsWith("\"") && json.length() > 1)
362-
return json.substring(1, json.length() - 2);
363-
else
364-
return json;
365-
}
366-
else {
367-
return JsonUtil.getJsonMapper().readValue(json, cls);
368-
}
369-
}
370-
catch (IOException e) {
371-
throw new ApiException(500, e.getMessage(), null, json);
345+
public String serialize(Object obj, String contentType) throws ApiException {
346+
if (contentType.startsWith("application/json")) {
347+
return json.serialize(obj);
348+
} else {
349+
throw new ApiException(400, "can not serialize object into Content-Type: " + contentType);
372350
}
373351
}
374352

375353
/**
376-
* Serialize the given Java object into JSON string.
354+
* Deserialize response body to Java object according to the Content-Type.
377355
*/
378-
public String serialize(Object obj) throws ApiException {
379-
try {
380-
if (obj != null)
381-
return JsonUtil.getJsonMapper().writeValueAsString(obj);
382-
else
383-
return null;
384-
}
385-
catch (Exception e) {
386-
throw new ApiException(500, e.getMessage());
356+
public <T> T deserialize(ClientResponse response, TypeRef returnType) throws ApiException {
357+
String contentType = null;
358+
List<String> contentTypes = response.getHeaders().get("Content-Type");
359+
if (contentTypes != null && !contentTypes.isEmpty())
360+
contentType = contentTypes.get(0);
361+
if (contentType == null)
362+
throw new ApiException(500, "missing Content-Type in response");
363+
364+
String body;
365+
if (response.hasEntity())
366+
body = (String) response.getEntity(String.class);
367+
else
368+
body = "";
369+
370+
if (contentType.startsWith("application/json")) {
371+
return json.deserialize(body, returnType);
372+
} else {
373+
throw new ApiException(500, "can not deserialize Content-Type: " + contentType);
387374
}
388375
}
389376

@@ -399,9 +386,10 @@ public class ApiClient {
399386
* @param accept The request's Accept header
400387
* @param contentType The request's Content-Type header
401388
* @param authNames The authentications to apply
389+
* @param returnType The return type into which to deserialize the response
402390
* @return The response body in type of string
403391
*/
404-
public String invokeAPI(String path, String method, List<Pair> queryParams, Object body, Map<String, String> headerParams, Map<String, Object> formParams, String accept, String contentType, String[] authNames) throws ApiException {
392+
public <T> T invokeAPI(String path, String method, List<Pair> queryParams, Object body, Map<String, String> headerParams, Map<String, Object> formParams, String accept, String contentType, String[] authNames, TypeRef returnType) throws ApiException {
405393
updateParamsForAuth(authNames, queryParams, headerParams);
406394
407395
Client client = getClient();
@@ -465,23 +453,23 @@ public class ApiClient {
465453
} else if (body instanceof FormDataMultiPart) {
466454
response = builder.type(contentType).post(ClientResponse.class, body);
467455
} else {
468-
response = builder.type(contentType).post(ClientResponse.class, serialize(body));
456+
response = builder.type(contentType).post(ClientResponse.class, serialize(body, contentType));
469457
}
470458
} else if ("PUT".equals(method)) {
471459
if (encodedFormParams != null) {
472460
response = builder.type(contentType).put(ClientResponse.class, encodedFormParams);
473461
} else if(body == null) {
474-
response = builder.put(ClientResponse.class, serialize(body));
462+
response = builder.put(ClientResponse.class, serialize(body, contentType));
475463
} else {
476-
response = builder.type(contentType).put(ClientResponse.class, serialize(body));
464+
response = builder.type(contentType).put(ClientResponse.class, serialize(body, contentType));
477465
}
478466
} else if ("DELETE".equals(method)) {
479467
if (encodedFormParams != null) {
480468
response = builder.type(contentType).delete(ClientResponse.class, encodedFormParams);
481469
} else if(body == null) {
482470
response = builder.delete(ClientResponse.class);
483471
} else {
484-
response = builder.type(contentType).delete(ClientResponse.class, serialize(body));
472+
response = builder.type(contentType).delete(ClientResponse.class, serialize(body, contentType));
485473
}
486474
} else {
487475
throw new ApiException(500, "unknown method type " + method);
@@ -490,11 +478,10 @@ public class ApiClient {
490478
if (response.getStatusInfo() == ClientResponse.Status.NO_CONTENT) {
491479
return null;
492480
} else if (response.getStatusInfo().getFamily() == Family.SUCCESSFUL) {
493-
if (response.hasEntity()) {
494-
return (String) response.getEntity(String.class);
495-
} else {
496-
return "";
497-
}
481+
if (returnType == null)
482+
return null;
483+
else
484+
return deserialize(response, returnType);
498485
} else {
499486
String message = "error";
500487
String respBody = null;
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
package {{invokerPackage}};
2+
3+
import com.fasterxml.jackson.annotation.*;
4+
import com.fasterxml.jackson.databind.*;
5+
import com.fasterxml.jackson.datatype.joda.*;
6+
7+
import java.io.IOException;
8+
9+
public class JSON {
10+
private ObjectMapper mapper;
11+
12+
public JSON() {
13+
mapper = new ObjectMapper();
14+
mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
15+
mapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
16+
mapper.registerModule(new JodaModule());
17+
}
18+
19+
/**
20+
* Serialize the given Java object into JSON string.
21+
*/
22+
public String serialize(Object obj) throws ApiException {
23+
try {
24+
if (obj != null)
25+
return mapper.writeValueAsString(obj);
26+
else
27+
return null;
28+
} catch (Exception e) {
29+
throw new ApiException(400, e.getMessage());
30+
}
31+
}
32+
33+
/**
34+
* Deserialize the given JSON string to Java object.
35+
*
36+
* @param body The JSON string
37+
* @param returnType The type to deserialize inot
38+
* @return The deserialized Java object
39+
*/
40+
public <T> T deserialize(String body, TypeRef returnType) throws ApiException {
41+
JavaType javaType = mapper.constructType(returnType.getType());
42+
try {
43+
return mapper.readValue(body, javaType);
44+
} catch (IOException e) {
45+
if (returnType.getType().equals(String.class))
46+
return (T) body;
47+
else
48+
throw new ApiException(500, e.getMessage(), null, body);
49+
}
50+
}
51+
}

modules/swagger-codegen/src/main/resources/Java/JsonUtil.mustache

Lines changed: 0 additions & 23 deletions
This file was deleted.
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
package {{invokerPackage}};
2+
3+
import java.lang.reflect.ParameterizedType;
4+
import java.lang.reflect.Type;
5+
6+
public class TypeRef<T> {
7+
private final Type type;
8+
9+
public TypeRef() {
10+
this.type = getGenericType(getClass());
11+
}
12+
13+
private static Type getGenericType(Class<?> klass) {
14+
Type superclass = klass.getGenericSuperclass();
15+
if (superclass instanceof Class) {
16+
throw new RuntimeException("No type parameter provided");
17+
}
18+
ParameterizedType parameterized = (ParameterizedType) superclass;
19+
return parameterized.getActualTypeArguments()[0];
20+
}
21+
22+
public Type getType() {
23+
return type;
24+
}
25+
}

modules/swagger-codegen/src/main/resources/Java/api.mustache

Lines changed: 8 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import {{invokerPackage}}.ApiException;
44
import {{invokerPackage}}.ApiClient;
55
import {{invokerPackage}}.Configuration;
66
import {{invokerPackage}}.Pair;
7+
import {{invokerPackage}}.TypeRef;
78

89
import {{modelPackage}}.*;
910

@@ -83,18 +84,13 @@ public class {{classname}} {
8384
};
8485
final String contentType = apiClient.selectHeaderContentType(contentTypes);
8586

86-
try {
87-
String[] authNames = new String[] { {{#authMethods}}"{{name}}"{{#hasMore}}, {{/hasMore}}{{/authMethods}} };
88-
String response = apiClient.invokeAPI(path, "{{httpMethod}}", queryParams, postBody, headerParams, formParams, accept, contentType, authNames);
89-
if(response != null){
90-
return {{#returnType}}({{{returnType}}}) apiClient.deserialize(response, "{{returnContainer}}", {{returnBaseType}}.class){{/returnType}};
91-
}
92-
else {
93-
return {{#returnType}}null{{/returnType}};
94-
}
95-
} catch (ApiException ex) {
96-
throw ex;
97-
}
87+
String[] authNames = new String[] { {{#authMethods}}"{{name}}"{{#hasMore}}, {{/hasMore}}{{/authMethods}} };
88+
{{#returnType}}
89+
TypeRef returnType = new TypeRef<{{{returnType}}}>() {};
90+
return apiClient.invokeAPI(path, "{{httpMethod}}", queryParams, postBody, headerParams, formParams, accept, contentType, authNames, returnType);
91+
{{/returnType}}{{^returnType}}
92+
apiClient.invokeAPI(path, "{{httpMethod}}", queryParams, postBody, headerParams, formParams, accept, contentType, authNames, null);
93+
{{/returnType}}
9894
}
9995
{{/operation}}
10096
}

0 commit comments

Comments
 (0)