From bfb4629ab78542bb4566ee58922bfbf1f2a1a5ce Mon Sep 17 00:00:00 2001 From: xhh Date: Tue, 4 Aug 2015 15:47:55 +0800 Subject: [PATCH] Java client: decouple JSON handling --- .../codegen/languages/JavaClientCodegen.java | 5 +- .../main/resources/Java/ApiClient.mustache | 87 ++++++++----------- .../src/main/resources/Java/JSON.mustache | 51 +++++++++++ .../src/main/resources/Java/JsonUtil.mustache | 23 ----- .../src/main/resources/Java/TypeRef.mustache | 25 ++++++ .../src/main/resources/Java/api.mustache | 20 ++--- 6 files changed, 124 insertions(+), 87 deletions(-) create mode 100644 modules/swagger-codegen/src/main/resources/Java/JSON.mustache delete mode 100644 modules/swagger-codegen/src/main/resources/Java/JsonUtil.mustache create mode 100644 modules/swagger-codegen/src/main/resources/Java/TypeRef.mustache diff --git a/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/JavaClientCodegen.java b/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/JavaClientCodegen.java index f272343a795..88266aa76cc 100644 --- a/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/JavaClientCodegen.java +++ b/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/JavaClientCodegen.java @@ -116,9 +116,10 @@ public class JavaClientCodegen extends DefaultCodegen implements CodegenConfig { supportingFiles.add(new SupportingFile("ApiClient.mustache", invokerFolder, "ApiClient.java")); supportingFiles.add(new SupportingFile("apiException.mustache", invokerFolder, "ApiException.java")); supportingFiles.add(new SupportingFile("Configuration.mustache", invokerFolder, "Configuration.java")); - supportingFiles.add(new SupportingFile("JsonUtil.mustache", invokerFolder, "JsonUtil.java")); - supportingFiles.add(new SupportingFile("StringUtil.mustache", invokerFolder, "StringUtil.java")); + supportingFiles.add(new SupportingFile("JSON.mustache", invokerFolder, "JSON.java")); supportingFiles.add(new SupportingFile("Pair.mustache", invokerFolder, "Pair.java")); + supportingFiles.add(new SupportingFile("StringUtil.mustache", invokerFolder, "StringUtil.java")); + supportingFiles.add(new SupportingFile("TypeRef.mustache", invokerFolder, "TypeRef.java")); final String authFolder = (sourceFolder + File.separator + invokerPackage + ".auth").replace(".", File.separator); supportingFiles.add(new SupportingFile("auth/Authentication.mustache", authFolder, "Authentication.java")); diff --git a/modules/swagger-codegen/src/main/resources/Java/ApiClient.mustache b/modules/swagger-codegen/src/main/resources/Java/ApiClient.mustache index 7a52b001684..b356dd36558 100644 --- a/modules/swagger-codegen/src/main/resources/Java/ApiClient.mustache +++ b/modules/swagger-codegen/src/main/resources/Java/ApiClient.mustache @@ -1,9 +1,7 @@ package {{invokerPackage}}; -import com.fasterxml.jackson.core.JsonGenerator.Feature; import com.fasterxml.jackson.databind.*; import com.fasterxml.jackson.annotation.*; -import com.fasterxml.jackson.databind.annotation.JsonSerialize; import com.sun.jersey.api.client.Client; import com.sun.jersey.api.client.ClientResponse; @@ -48,6 +46,7 @@ public class ApiClient { private Map defaultHeaderMap = new HashMap(); private boolean debugging = false; private String basePath = "{{basePath}}"; + private JSON json = new JSON(); private Map authentications; @@ -340,50 +339,38 @@ public class ApiClient { } /** - * Deserialize the given JSON string to Java object. - * - * @param json The JSON string - * @param containerType The container type, one of "list", "array" or "" - * @param cls The type of the Java object - * @return The deserialized Java object + * Serialize the given Java object into string according the given + * Content-Type (only JSON is supported for now). */ - public Object deserialize(String json, String containerType, Class cls) throws ApiException { - if(null != containerType) { - containerType = containerType.toLowerCase(); - } - try{ - if("list".equals(containerType) || "array".equals(containerType)) { - JavaType typeInfo = JsonUtil.getJsonMapper().getTypeFactory().constructCollectionType(List.class, cls); - List response = (List) JsonUtil.getJsonMapper().readValue(json, typeInfo); - return response; - } - else if(String.class.equals(cls)) { - if(json != null && json.startsWith("\"") && json.endsWith("\"") && json.length() > 1) - return json.substring(1, json.length() - 2); - else - return json; - } - else { - return JsonUtil.getJsonMapper().readValue(json, cls); - } - } - catch (IOException e) { - throw new ApiException(500, e.getMessage(), null, json); + public String serialize(Object obj, String contentType) throws ApiException { + if (contentType.startsWith("application/json")) { + return json.serialize(obj); + } else { + throw new ApiException(400, "can not serialize object into Content-Type: " + contentType); } } /** - * Serialize the given Java object into JSON string. + * Deserialize response body to Java object according to the Content-Type. */ - public String serialize(Object obj) throws ApiException { - try { - if (obj != null) - return JsonUtil.getJsonMapper().writeValueAsString(obj); - else - return null; - } - catch (Exception e) { - throw new ApiException(500, e.getMessage()); + public T deserialize(ClientResponse response, TypeRef returnType) throws ApiException { + String contentType = null; + List contentTypes = response.getHeaders().get("Content-Type"); + if (contentTypes != null && !contentTypes.isEmpty()) + contentType = contentTypes.get(0); + if (contentType == null) + throw new ApiException(500, "missing Content-Type in response"); + + String body; + if (response.hasEntity()) + body = (String) response.getEntity(String.class); + else + body = ""; + + if (contentType.startsWith("application/json")) { + return json.deserialize(body, returnType); + } else { + throw new ApiException(500, "can not deserialize Content-Type: " + contentType); } } @@ -399,9 +386,10 @@ public class ApiClient { * @param accept The request's Accept header * @param contentType The request's Content-Type header * @param authNames The authentications to apply + * @param returnType The return type into which to deserialize the response * @return The response body in type of string */ - public String invokeAPI(String path, String method, List queryParams, Object body, Map headerParams, Map formParams, String accept, String contentType, String[] authNames) throws ApiException { + public T invokeAPI(String path, String method, List queryParams, Object body, Map headerParams, Map formParams, String accept, String contentType, String[] authNames, TypeRef returnType) throws ApiException { updateParamsForAuth(authNames, queryParams, headerParams); Client client = getClient(); @@ -465,15 +453,15 @@ public class ApiClient { } else if (body instanceof FormDataMultiPart) { response = builder.type(contentType).post(ClientResponse.class, body); } else { - response = builder.type(contentType).post(ClientResponse.class, serialize(body)); + response = builder.type(contentType).post(ClientResponse.class, serialize(body, contentType)); } } else if ("PUT".equals(method)) { if (encodedFormParams != null) { response = builder.type(contentType).put(ClientResponse.class, encodedFormParams); } else if(body == null) { - response = builder.put(ClientResponse.class, serialize(body)); + response = builder.put(ClientResponse.class, serialize(body, contentType)); } else { - response = builder.type(contentType).put(ClientResponse.class, serialize(body)); + response = builder.type(contentType).put(ClientResponse.class, serialize(body, contentType)); } } else if ("DELETE".equals(method)) { if (encodedFormParams != null) { @@ -481,7 +469,7 @@ public class ApiClient { } else if(body == null) { response = builder.delete(ClientResponse.class); } else { - response = builder.type(contentType).delete(ClientResponse.class, serialize(body)); + response = builder.type(contentType).delete(ClientResponse.class, serialize(body, contentType)); } } else { throw new ApiException(500, "unknown method type " + method); @@ -490,11 +478,10 @@ public class ApiClient { if (response.getStatusInfo() == ClientResponse.Status.NO_CONTENT) { return null; } else if (response.getStatusInfo().getFamily() == Family.SUCCESSFUL) { - if (response.hasEntity()) { - return (String) response.getEntity(String.class); - } else { - return ""; - } + if (returnType == null) + return null; + else + return deserialize(response, returnType); } else { String message = "error"; String respBody = null; diff --git a/modules/swagger-codegen/src/main/resources/Java/JSON.mustache b/modules/swagger-codegen/src/main/resources/Java/JSON.mustache new file mode 100644 index 00000000000..842920f2662 --- /dev/null +++ b/modules/swagger-codegen/src/main/resources/Java/JSON.mustache @@ -0,0 +1,51 @@ +package {{invokerPackage}}; + +import com.fasterxml.jackson.annotation.*; +import com.fasterxml.jackson.databind.*; +import com.fasterxml.jackson.datatype.joda.*; + +import java.io.IOException; + +public class JSON { + private ObjectMapper mapper; + + public JSON() { + mapper = new ObjectMapper(); + mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); + mapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS); + mapper.registerModule(new JodaModule()); + } + + /** + * Serialize the given Java object into JSON string. + */ + public String serialize(Object obj) throws ApiException { + try { + if (obj != null) + return mapper.writeValueAsString(obj); + else + return null; + } catch (Exception e) { + throw new ApiException(400, e.getMessage()); + } + } + + /** + * Deserialize the given JSON string to Java object. + * + * @param body The JSON string + * @param returnType The type to deserialize inot + * @return The deserialized Java object + */ + public T deserialize(String body, TypeRef returnType) throws ApiException { + JavaType javaType = mapper.constructType(returnType.getType()); + try { + return mapper.readValue(body, javaType); + } catch (IOException e) { + if (returnType.getType().equals(String.class)) + return (T) body; + else + throw new ApiException(500, e.getMessage(), null, body); + } + } +} diff --git a/modules/swagger-codegen/src/main/resources/Java/JsonUtil.mustache b/modules/swagger-codegen/src/main/resources/Java/JsonUtil.mustache deleted file mode 100644 index 29d5f55ecee..00000000000 --- a/modules/swagger-codegen/src/main/resources/Java/JsonUtil.mustache +++ /dev/null @@ -1,23 +0,0 @@ -package {{invokerPackage}}; - -import com.fasterxml.jackson.annotation.*; -import com.fasterxml.jackson.databind.*; -import com.fasterxml.jackson.databind.annotation.JsonSerialize; -import com.fasterxml.jackson.core.JsonGenerator.Feature; - -import com.fasterxml.jackson.datatype.joda.*; - -public class JsonUtil { - public static ObjectMapper mapper; - - static { - mapper = new ObjectMapper(); - mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); - mapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS); - mapper.registerModule(new JodaModule()); - } - - public static ObjectMapper getJsonMapper() { - return mapper; - } -} diff --git a/modules/swagger-codegen/src/main/resources/Java/TypeRef.mustache b/modules/swagger-codegen/src/main/resources/Java/TypeRef.mustache new file mode 100644 index 00000000000..5de0b840aa7 --- /dev/null +++ b/modules/swagger-codegen/src/main/resources/Java/TypeRef.mustache @@ -0,0 +1,25 @@ +package {{invokerPackage}}; + +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; + +public class TypeRef { + private final Type type; + + public TypeRef() { + this.type = getGenericType(getClass()); + } + + private static Type getGenericType(Class klass) { + Type superclass = klass.getGenericSuperclass(); + if (superclass instanceof Class) { + throw new RuntimeException("No type parameter provided"); + } + ParameterizedType parameterized = (ParameterizedType) superclass; + return parameterized.getActualTypeArguments()[0]; + } + + public Type getType() { + return type; + } +} diff --git a/modules/swagger-codegen/src/main/resources/Java/api.mustache b/modules/swagger-codegen/src/main/resources/Java/api.mustache index 29a56119e76..4168c3a173a 100644 --- a/modules/swagger-codegen/src/main/resources/Java/api.mustache +++ b/modules/swagger-codegen/src/main/resources/Java/api.mustache @@ -4,6 +4,7 @@ import {{invokerPackage}}.ApiException; import {{invokerPackage}}.ApiClient; import {{invokerPackage}}.Configuration; import {{invokerPackage}}.Pair; +import {{invokerPackage}}.TypeRef; import {{modelPackage}}.*; @@ -83,18 +84,13 @@ public class {{classname}} { }; final String contentType = apiClient.selectHeaderContentType(contentTypes); - try { - String[] authNames = new String[] { {{#authMethods}}"{{name}}"{{#hasMore}}, {{/hasMore}}{{/authMethods}} }; - String response = apiClient.invokeAPI(path, "{{httpMethod}}", queryParams, postBody, headerParams, formParams, accept, contentType, authNames); - if(response != null){ - return {{#returnType}}({{{returnType}}}) apiClient.deserialize(response, "{{returnContainer}}", {{returnBaseType}}.class){{/returnType}}; - } - else { - return {{#returnType}}null{{/returnType}}; - } - } catch (ApiException ex) { - throw ex; - } + String[] authNames = new String[] { {{#authMethods}}"{{name}}"{{#hasMore}}, {{/hasMore}}{{/authMethods}} }; + {{#returnType}} + TypeRef returnType = new TypeRef<{{{returnType}}}>() {}; + return apiClient.invokeAPI(path, "{{httpMethod}}", queryParams, postBody, headerParams, formParams, accept, contentType, authNames, returnType); + {{/returnType}}{{^returnType}} + apiClient.invokeAPI(path, "{{httpMethod}}", queryParams, postBody, headerParams, formParams, accept, contentType, authNames, null); + {{/returnType}} } {{/operation}} }