Java tutorial
package io.swagger.parser.util; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.node.ArrayNode; import com.fasterxml.jackson.databind.node.JsonNodeType; import com.fasterxml.jackson.databind.node.NumericNode; import com.fasterxml.jackson.databind.node.ObjectNode; import com.fasterxml.jackson.databind.node.TextNode; import io.swagger.models.ArrayModel; import io.swagger.models.ComposedModel; import io.swagger.models.Contact; import io.swagger.models.ExternalDocs; import io.swagger.models.Info; import io.swagger.models.License; import io.swagger.models.Model; import io.swagger.models.ModelImpl; import io.swagger.models.Operation; import io.swagger.models.Path; import io.swagger.models.RefModel; import io.swagger.models.RefPath; import io.swagger.models.RefResponse; import io.swagger.models.Response; import io.swagger.models.Scheme; import io.swagger.models.SecurityRequirement; import io.swagger.models.Swagger; import io.swagger.models.Tag; import io.swagger.models.Xml; import io.swagger.models.auth.ApiKeyAuthDefinition; import io.swagger.models.auth.BasicAuthDefinition; import io.swagger.models.auth.In; import io.swagger.models.auth.OAuth2Definition; import io.swagger.models.auth.SecuritySchemeDefinition; import io.swagger.models.parameters.AbstractSerializableParameter; import io.swagger.models.parameters.FormParameter; import io.swagger.models.parameters.HeaderParameter; import io.swagger.models.parameters.Parameter; import io.swagger.models.parameters.PathParameter; import io.swagger.models.parameters.QueryParameter; import io.swagger.models.parameters.RefParameter; import io.swagger.models.properties.Property; import io.swagger.models.properties.PropertyBuilder; import io.swagger.util.Json; import java.util.*; import static io.swagger.models.properties.PropertyBuilder.PropertyId.*; public class SwaggerDeserializer { static Set<String> ROOT_KEYS = new HashSet<String>(Arrays.asList("swagger", "info", "host", "basePath", "schemes", "consumes", "produces", "paths", "definitions", "parameters", "responses", "securityDefinitions", "security", "tags", "externalDocs")); static Set<String> EXTERNAL_DOCS_KEYS = new HashSet<String>(Arrays.asList("description", "url")); static Set<String> SCHEMA_KEYS = new HashSet<String>( Arrays.asList("example", "$ref", "format", "title", "description", "default", "multipleOf", "maximum", "exclusiveMaximum", "minimum", "exclusiveMinimum", "maxLength", "minLength", "pattern", "maxItems", "minItems", "uniqueItems", "maxProperties", "minProperties", "required", "enum", "type", "items", "allOf", "properties", "additionalProperties", "xml")); static Set<String> INFO_KEYS = new HashSet<String>( Arrays.asList("title", "description", "termsOfService", "contact", "license", "version")); static Set<String> TAG_KEYS = new HashSet<String>(Arrays.asList("description", "name", "externalDocs")); static Set<String> RESPONSE_KEYS = new HashSet<String>( Arrays.asList("description", "schema", "headers", "examples")); static Set<String> CONTACT_KEYS = new HashSet<String>(Arrays.asList("name", "url", "email")); static Set<String> LICENSE_KEYS = new HashSet<String>(Arrays.asList("name", "url")); static Set<String> REF_MODEL_KEYS = new HashSet<String>(Arrays.asList("$ref")); static Set<String> PATH_KEYS = new HashSet<String>( Arrays.asList("$ref", "get", "put", "post", "delete", "head", "patch", "options", "parameters")); static Set<String> OPERATION_KEYS = new HashSet<String>( Arrays.asList("scheme", "tags", "summary", "description", "externalDocs", "operationId", "consumes", "produces", "parameters", "responses", "schemes", "deprecated", "security")); static Set<String> PARAMETER_KEYS = new HashSet<String>(Arrays.asList("name", "in", "description", "required", "type", "format", "allowEmptyValue", "items", "collectionFormat", "default", "maximum", "exclusiveMaximum", "minimum", "exclusiveMinimum", "maxLenth", "minLength", "pattern", "maxItems", "minItems", "uniqueItems", "enum", "multipleOf")); static Set<String> BODY_PARAMETER_KEYS = new HashSet<String>( Arrays.asList("name", "in", "description", "required", "schema")); public SwaggerDeserializationResult deserialize(JsonNode rootNode) { SwaggerDeserializationResult result = new SwaggerDeserializationResult(); ParseResult rootParse = new ParseResult(); Swagger swagger = parseRoot(rootNode, rootParse); result.setSwagger(swagger); result.setMessages(rootParse.getMessages()); return result; } public Swagger parseRoot(JsonNode node, ParseResult result) { String location = ""; Swagger swagger = new Swagger(); if (node.getNodeType().equals(JsonNodeType.OBJECT)) { ObjectNode on = (ObjectNode) node; Iterator<JsonNode> it = null; // required String value = getString("swagger", on, true, location, result); swagger.setSwagger(value); ObjectNode obj = getObject("info", on, true, "", result); if (obj != null) { Info info = info(obj, "info", result); swagger.info(info); } // optional value = getString("host", on, false, location, result); swagger.setHost(value); value = getString("basePath", on, false, location, result); swagger.setBasePath(value); ArrayNode array = getArray("schemes", on, false, location, result); if (array != null) { it = array.iterator(); while (it.hasNext()) { JsonNode n = it.next(); String s = getString(n, location + ".schemes", result); if (s != null) { Scheme scheme = Scheme.forValue(s); if (scheme != null) { swagger.scheme(scheme); } } } } array = getArray("consumes", on, false, location, result); if (array != null) { it = array.iterator(); while (it.hasNext()) { JsonNode n = it.next(); String s = getString(n, location + ".consumes", result); if (s != null) { swagger.consumes(s); } } } array = getArray("produces", on, false, location, result); if (array != null) { it = array.iterator(); while (it.hasNext()) { JsonNode n = it.next(); String s = getString(n, location + ".produces", result); if (s != null) { swagger.produces(s); } } } obj = getObject("paths", on, true, location, result); Map<String, Path> paths = paths(obj, "paths", result); swagger.paths(paths); obj = getObject("definitions", on, false, location, result); Map<String, Model> definitions = definitions(obj, "definitions", result); swagger.setDefinitions(definitions); obj = getObject("parameters", on, false, location, result); // TODO: parse Map<String, Parameter> parameters = Json.mapper().convertValue(obj, Json.mapper().getTypeFactory().constructMapType(Map.class, String.class, Parameter.class)); swagger.setParameters(parameters); obj = getObject("responses", on, false, location, result); Map<String, Response> responses = responses(obj, "responses", result); swagger.responses(responses); obj = getObject("securityDefinitions", on, false, location, result); Map<String, SecuritySchemeDefinition> securityDefinitions = securityDefinitions(obj, location, result); swagger.setSecurityDefinitions(securityDefinitions); array = getArray("security", on, false, location, result); List<SecurityRequirement> security = securityRequirements(array, location, result); swagger.setSecurity(security); array = getArray("tags", on, false, location, result); List<Tag> tags = tags(array, location, result); swagger.tags(tags); obj = getObject("externalDocs", on, false, location, result); ExternalDocs docs = externalDocs(obj, location, result); swagger.externalDocs(docs); // extra keys Set<String> keys = getKeys(on); for (String key : keys) { if (key.startsWith("x-")) { swagger.vendorExtension(key, extension(on.get(key))); } else if (!ROOT_KEYS.contains(key)) { result.extra(location, key, node.get(key)); } } } return swagger; } public Map<String, Path> paths(ObjectNode obj, String location, ParseResult result) { Map<String, Path> output = new LinkedHashMap<>(); if (obj == null) { return null; } Set<String> pathKeys = getKeys(obj); for (String pathName : pathKeys) { JsonNode pathValue = obj.get(pathName); if (!pathValue.getNodeType().equals(JsonNodeType.OBJECT)) { result.invalidType(location, pathName, "object", pathValue); } else { ObjectNode path = (ObjectNode) pathValue; Path pathObj = path(path, location + ".'" + pathName + "'", result); output.put(pathName, pathObj); } } return output; } public Path path(ObjectNode obj, String location, ParseResult result) { boolean hasRef = false; Path output = null; if (obj.get("$ref") != null) { JsonNode ref = obj.get("$ref"); if (ref.getNodeType().equals(JsonNodeType.STRING)) { return pathRef((TextNode) ref, location, result); } else if (ref.getNodeType().equals(JsonNodeType.OBJECT)) { ObjectNode on = (ObjectNode) ref; // extra keys Set<String> keys = getKeys(on); for (String key : keys) { result.extra(location, key, on.get(key)); } } return null; } Path path = new Path(); ArrayNode parameters = getArray("parameters", obj, false, location, result); path.setParameters(parameters(parameters, location, result)); ObjectNode on = getObject("get", obj, false, location, result); if (on != null) { Operation op = operation(on, location + "(get)", result); if (op != null) { path.setGet(op); } } on = getObject("put", obj, false, location, result); if (on != null) { Operation op = operation(on, location + "(put)", result); if (op != null) { path.setPut(op); } } on = getObject("post", obj, false, location, result); if (on != null) { Operation op = operation(on, location + "(post)", result); if (op != null) { path.setPost(op); } } on = getObject("head", obj, false, location, result); if (on != null) { Operation op = operation(on, location + "(head)", result); if (op != null) { path.setHead(op); } } on = getObject("delete", obj, false, location, result); if (on != null) { Operation op = operation(on, location + "(delete)", result); if (op != null) { path.setDelete(op); } } on = getObject("patch", obj, false, location, result); if (on != null) { Operation op = operation(on, location + "(patch)", result); if (op != null) { path.setPatch(op); } } on = getObject("options", obj, false, location, result); if (on != null) { Operation op = operation(on, location + "(options)", result); if (op != null) { path.setOptions(op); } } // extra keys Set<String> keys = getKeys(obj); for (String key : keys) { if (key.startsWith("x-")) { path.setVendorExtension(key, extension(obj.get(key))); } else if (!PATH_KEYS.contains(key)) { result.extra(location, key, obj.get(key)); } } return path; } public Operation operation(ObjectNode obj, String location, ParseResult result) { if (obj == null) { return null; } Operation output = new Operation(); ArrayNode array = getArray("tags", obj, false, location, result); List<String> tags = tagStrings(array, location, result); if (tags != null) { output.tags(tags); } String value = getString("summary", obj, false, location, result); output.summary(value); value = getString("description", obj, false, location, result); output.description(value); ObjectNode externalDocs = getObject("externalDocs", obj, false, location, result); ExternalDocs docs = externalDocs(externalDocs, location, result); output.setExternalDocs(docs); value = getString("operationId", obj, false, location, result); output.operationId(value); array = getArray("consumes", obj, false, location, result); if (array != null) { if (array.size() == 0) { output.consumes(Collections.<String>emptyList()); } else { Iterator<JsonNode> it = array.iterator(); while (it.hasNext()) { JsonNode n = it.next(); String s = getString(n, location + ".consumes", result); if (s != null) { output.consumes(s); } } } } array = getArray("produces", obj, false, location, result); if (array != null) { if (array.size() == 0) { output.produces(Collections.<String>emptyList()); } else { Iterator<JsonNode> it = array.iterator(); while (it.hasNext()) { JsonNode n = it.next(); String s = getString(n, location + ".produces", result); if (s != null) { output.produces(s); } } } } ArrayNode parameters = getArray("parameters", obj, false, location, result); output.setParameters(parameters(parameters, location, result)); ObjectNode responses = getObject("responses", obj, true, location, result); output.setResponses(responses(responses, location, result)); array = getArray("schemes", obj, false, location, result); if (array != null) { Iterator<JsonNode> it = array.iterator(); while (it.hasNext()) { JsonNode n = it.next(); String s = getString(n, location + ".schemes", result); if (s != null) { Scheme scheme = Scheme.forValue(s); if (scheme != null) { output.scheme(scheme); } } } } Boolean deprecated = getBoolean("deprecated", obj, false, location, result); if (deprecated != null) { output.setDeprecated(deprecated); } array = getArray("security", obj, false, location, result); List<SecurityRequirement> security = securityRequirements(array, location, result); if (security != null) { List<Map<String, List<String>>> ss = new ArrayList<>(); for (SecurityRequirement s : security) { if (s.getRequirements() != null && s.getRequirements().size() > 0) { ss.add(s.getRequirements()); } } output.setSecurity(ss); } // extra keys Set<String> keys = getKeys(obj); for (String key : keys) { if (key.startsWith("x-")) { output.setVendorExtension(key, extension(obj.get(key))); } else if (!OPERATION_KEYS.contains(key)) { result.extra(location, key, obj.get(key)); } } return output; } public Boolean getBoolean(String key, ObjectNode node, boolean required, String location, ParseResult result) { Boolean value = null; JsonNode v = node.get(key); if (node == null || v == null) { if (required) { result.missing(location, key); result.invalid(); } } else { if (v.getNodeType().equals(JsonNodeType.BOOLEAN)) { value = v.asBoolean(); } else if (v.getNodeType().equals(JsonNodeType.STRING)) { String stringValue = v.textValue(); return Boolean.parseBoolean(stringValue); } } return value; } public List<Parameter> parameters(ArrayNode obj, String location, ParseResult result) { List<Parameter> output = new ArrayList<Parameter>(); if (obj == null) { return output; } for (JsonNode item : obj) { if (item.getNodeType().equals(JsonNodeType.OBJECT)) { Parameter param = parameter((ObjectNode) item, location, result); if (param != null) { output.add(param); } } } return output; } public Parameter parameter(ObjectNode obj, String location, ParseResult result) { if (obj == null) { return null; } Parameter output = null; JsonNode ref = obj.get("$ref"); if (ref != null) { if (ref.getNodeType().equals(JsonNodeType.STRING)) { return refParameter((TextNode) ref, location, result); } else { result.invalidType(location, "$ref", "string", obj); return null; } } String l = null; JsonNode ln = obj.get("name"); if (ln != null) { l = ln.asText(); } else { l = "['unknown']"; } location += ".[" + l + "]"; String value = getString("in", obj, true, location, result); if (value != null) { String type = getString("type", obj, false, location, result); String format = getString("format", obj, false, location, result); AbstractSerializableParameter<?> sp = null; if ("query".equals(value)) { sp = new QueryParameter(); } else if ("header".equals(value)) { sp = new HeaderParameter(); } else if ("path".equals(value)) { sp = new PathParameter(); } else if ("formData".equals(value)) { sp = new FormParameter(); } if (sp != null) { // type is mandatory when sp != null getString("type", obj, true, location, result); Map<PropertyBuilder.PropertyId, Object> map = new HashMap<PropertyBuilder.PropertyId, Object>(); map.put(TYPE, type); map.put(FORMAT, format); String defaultValue = getString("default", obj, false, location, result); map.put(DEFAULT, defaultValue); sp.setDefault(defaultValue); Double dbl = getDouble("maximum", obj, false, location, result); if (dbl != null) { map.put(MAXIMUM, dbl); sp.setMaximum(dbl); } Boolean bl = getBoolean("exclusiveMaximum", obj, false, location, result); if (bl != null) { map.put(EXCLUSIVE_MAXIMUM, bl); sp.setExclusiveMaximum(bl); } dbl = getDouble("minimum", obj, false, location, result); if (dbl != null) { map.put(MINIMUM, dbl); sp.setMinimum(dbl); } bl = getBoolean("exclusiveMinimum", obj, false, location, result); if (bl != null) { map.put(EXCLUSIVE_MINIMUM, bl); sp.setExclusiveMinimum(bl); } map.put(MAX_LENGTH, getInteger("maxLength", obj, false, location, result)); map.put(MIN_LENGTH, getInteger("minLength", obj, false, location, result)); String pat = getString("pattern", obj, false, location, result); map.put(PATTERN, pat); sp.setPattern(pat); Integer iv = getInteger("maxItems", obj, false, location, result); map.put(MAX_ITEMS, iv); sp.setMaxItems(iv); iv = getInteger("minItems", obj, false, location, result); map.put(MIN_ITEMS, iv); sp.setMinItems(iv); map.put(UNIQUE_ITEMS, getBoolean("uniqueItems", obj, false, location, result)); ArrayNode an = getArray("enum", obj, false, location, result); if (an != null) { List<String> _enum = new ArrayList<String>(); for (JsonNode n : an) { _enum.add(n.textValue()); } sp.setEnum(_enum); map.put(ENUM, _enum); } Property prop = PropertyBuilder.build(type, format, map); if (prop != null) { sp.setProperty(prop); ObjectNode items = getObject("items", obj, false, location, result); if (items != null) { Property inner = schema(null, items, location, result); sp.setItems(inner); } } Set<String> keys = getKeys(obj); for (String key : keys) { if (key.startsWith("x-")) { sp.setVendorExtension(key, extension(obj.get(key))); } else if (!PARAMETER_KEYS.contains(key)) { result.extra(location, key, obj.get(key)); } } String collectionFormat = getString("collectionFormat", obj, false, location, result); sp.setCollectionFormat(collectionFormat); output = sp; } else if ("body".equals(value)) { output = Json.mapper().convertValue(obj, Parameter.class); } if (output != null) { value = getString("name", obj, true, location, result); output.setName(value); value = getString("description", obj, false, location, result); output.setDescription(value); Boolean required = getBoolean("required", obj, false, location, result); if (required != null) { output.setRequired(required); } } } return output; } private Property schema(Map<String, Object> schemaItems, JsonNode obj, String location, ParseResult result) { return Json.mapper().convertValue(obj, Property.class); } public RefParameter refParameter(TextNode obj, String location, ParseResult result) { return new RefParameter(obj.asText()); } public RefResponse refResponse(TextNode obj, String location, ParseResult result) { return new RefResponse(obj.asText()); } public Path pathRef(TextNode ref, String location, ParseResult result) { RefPath output = new RefPath(); output.set$ref(ref.textValue()); return output; } public Map<String, Model> definitions(ObjectNode node, String location, ParseResult result) { if (node == null) return null; Set<String> schemas = getKeys(node); Map<String, Model> output = new LinkedHashMap<String, Model>(); for (String schemaName : schemas) { JsonNode schema = node.get(schemaName); if (schema.getNodeType().equals(JsonNodeType.OBJECT)) { Model model = definition((ObjectNode) schema, location + "." + schemaName, result); if (model != null) { output.put(schemaName, model); } } else { result.invalidType(location, schemaName, "object", schema); } } return output; } public Model definition(ObjectNode node, String location, ParseResult result) { if (node == null) { result.missing(location, "empty schema"); } if (node.get("$ref") != null) { return refModel(node, location, result); } if (node.get("allOf") != null) { return allOfModel(node, location, result); } Model model = null; String value = null; String type = getString("type", node, false, location, result); Model m = new ModelImpl(); if ("array".equals(type)) { ArrayModel am = new ArrayModel(); ObjectNode propertyNode = getObject("properties", node, false, location, result); Map<String, Property> properties = properties(propertyNode, location, result); am.setProperties(properties); ObjectNode itemsNode = getObject("items", node, false, location, result); Property items = property(itemsNode, location, result); if (items != null) { am.items(items); } model = am; } else { ModelImpl impl = new ModelImpl(); impl.setType(value); JsonNode ap = node.get("additionalProperties"); if (ap != null && ap.getNodeType().equals(JsonNodeType.OBJECT)) { impl.setAdditionalProperties(Json.mapper().convertValue(ap, Property.class)); } value = getString("default", node, false, location, result); impl.setDefaultValue(value); value = getString("format", node, false, location, result); impl.setFormat(value); value = getString("discriminator", node, false, location, result); impl.setDiscriminator(value); JsonNode xml = node.get("xml"); if (xml != null) { impl.setXml(Json.mapper().convertValue(xml, Xml.class)); } ObjectNode externalDocs = getObject("externalDocs", node, false, location, result); ExternalDocs docs = externalDocs(externalDocs, location, result); impl.setExternalDocs(docs); ObjectNode properties = getObject("properties", node, true, location, result); if (properties != null) { Set<String> propertyNames = getKeys(properties); for (String propertyName : propertyNames) { JsonNode propertyNode = properties.get(propertyName); if (propertyNode.getNodeType().equals(JsonNodeType.OBJECT)) { ObjectNode on = (ObjectNode) propertyNode; Property property = property(on, location, result); impl.property(propertyName, property); } else { result.invalidType(location, "properties", "object", propertyNode); } } } // need to set properties first ArrayNode required = getArray("required", node, false, location, result); if (required != null) { List<String> requiredProperties = new ArrayList<String>(); for (JsonNode n : required) { if (n.getNodeType().equals(JsonNodeType.STRING)) { requiredProperties.add(((TextNode) n).textValue()); } else { result.invalidType(location, "required", "string", n); } } if (requiredProperties.size() > 0) { impl.setRequired(requiredProperties); } } // extra keys Set<String> keys = getKeys(node); for (String key : keys) { if (key.startsWith("x-")) { impl.setVendorExtension(key, extension(node.get(key))); } else if (!SCHEMA_KEYS.contains(key)) { result.extra(location, key, node.get(key)); } } if ("{ }".equals(Json.pretty(impl))) return null; model = impl; } JsonNode exampleNode = node.get("example"); if (exampleNode != null) { // we support text or object nodes if (exampleNode.getNodeType().equals(JsonNodeType.OBJECT)) { ObjectNode on = getObject("example", node, false, location, result); if (on != null) { model.setExample(on); } } else { model.setExample(exampleNode.asText()); } } if (model != null) { value = getString("description", node, false, location, result); model.setDescription(value); value = getString("title", node, false, location, result); model.setTitle(value); } return model; } public Object extension(JsonNode jsonNode) { if (jsonNode.getNodeType().equals(JsonNodeType.BOOLEAN)) { return jsonNode.asBoolean(); } if (jsonNode.getNodeType().equals(JsonNodeType.STRING)) { return jsonNode.asText(); } if (jsonNode.getNodeType().equals(JsonNodeType.NUMBER)) { NumericNode n = (NumericNode) jsonNode; if (n.isLong()) { return jsonNode.asLong(); } if (n.isInt()) { return jsonNode.asInt(); } if (n.isBigDecimal()) { return jsonNode.textValue(); } if (n.isBoolean()) { return jsonNode.asBoolean(); } if (n.isFloat()) { return jsonNode.floatValue(); } if (n.isDouble()) { return jsonNode.doubleValue(); } if (n.isShort()) { return jsonNode.intValue(); } return jsonNode.asText(); } if (jsonNode.getNodeType().equals(JsonNodeType.ARRAY)) { ArrayNode an = (ArrayNode) jsonNode; List<Object> o = new ArrayList<Object>(); for (JsonNode i : an) { Object obj = extension(i); if (obj != null) { o.add(obj); } } return o; } return jsonNode; } public Model allOfModel(ObjectNode node, String location, ParseResult result) { JsonNode sub = node.get("$ref"); JsonNode allOf = node.get("allOf"); if (sub != null) { if (sub.getNodeType().equals(JsonNodeType.OBJECT)) { return refModel((ObjectNode) sub, location, result); } else { result.invalidType(location, "$ref", "object", sub); return null; } } else if (allOf != null) { ComposedModel model = null; // we only support one parent, no multiple inheritance or composition if (allOf.getNodeType().equals(JsonNodeType.ARRAY)) { model = new ComposedModel(); int pos = 0; for (JsonNode part : allOf) { if (part.getNodeType().equals(JsonNodeType.OBJECT)) { Model segment = definition((ObjectNode) part, location, result); if (segment != null) { model.getAllOf().add(segment); } } else { result.invalidType(location, "allOf[" + pos + "]", "object", part); } pos++; } List<Model> allComponents = model.getAllOf(); if (allComponents.size() >= 1) { model.setParent(allComponents.get(0)); if (allComponents.size() >= 2) { model.setChild(allComponents.get(allComponents.size() - 1)); List<RefModel> interfaces = new ArrayList<RefModel>(); int size = allComponents.size(); for (Model m : allComponents.subList(1, size - 1)) { if (m instanceof RefModel) { RefModel ref = (RefModel) m; interfaces.add(ref); } } model.setInterfaces(interfaces); } else { model.setChild(new ModelImpl()); } } return model; } else { result.invalidType(location, "allOf", "array", allOf); } return model; } return null; } public Map<String, Property> properties(ObjectNode node, String location, ParseResult result) { if (node == null) { return null; } Map<String, Property> output = new LinkedHashMap<String, Property>(); Set<String> keys = getKeys(node); for (String propertyName : keys) { JsonNode propertyNode = node.get(propertyName); if (propertyNode.getNodeType().equals(JsonNodeType.OBJECT)) { Property property = property((ObjectNode) propertyNode, location, result); output.put(propertyName, property); } else { result.invalidType(location, propertyName, "object", propertyNode); } } return output; } public Property property(ObjectNode node, String location, ParseResult result) { Property output = Json.mapper().convertValue(node, Property.class); return output; } public RefModel refModel(ObjectNode node, String location, ParseResult result) { RefModel output = new RefModel(); if (node.getNodeType().equals(JsonNodeType.OBJECT)) { String refValue = ((TextNode) node.get("$ref")).textValue(); output.set$ref(refValue); } else { result.invalidType(location, "$ref", "object", node); return null; } // extra keys Set<String> keys = getKeys(node); for (String key : keys) { if (!REF_MODEL_KEYS.contains(key)) { result.extra(location, key, node.get(key)); } } return output; } public Map<String, Response> responses(ObjectNode node, String location, ParseResult result) { if (node == null) return null; Map<String, Response> output = new TreeMap<String, Response>(); Set<String> keys = getKeys(node); for (String key : keys) { ObjectNode obj = getObject(key, node, false, location + ".responses", result); Response response = response(obj, location + "." + key, result); output.put(key, response); } return output; } public Response response(ObjectNode node, String location, ParseResult result) { if (node == null) return null; Response output = new Response(); JsonNode ref = node.get("$ref"); if (ref != null) { if (ref.getNodeType().equals(JsonNodeType.STRING)) { return refResponse((TextNode) ref, location, result); } else { result.invalidType(location, "$ref", "string", node); return null; } } String value = getString("description", node, true, location, result); output.description(value); ObjectNode schema = getObject("schema", node, false, location, result); if (schema != null) { output.schema(Json.mapper().convertValue(schema, Property.class)); } ObjectNode headersNode = getObject("headers", node, false, location, result); if (headersNode != null) { // TODO Map<String, Property> headers = Json.mapper().convertValue(headersNode, Json.mapper().getTypeFactory().constructMapType(Map.class, String.class, Property.class)); output.headers(headers); } ObjectNode examplesNode = getObject("examples", node, false, location, result); if (examplesNode != null) { Map<String, Object> examples = Json.mapper().convertValue(examplesNode, Json.mapper().getTypeFactory().constructMapType(Map.class, String.class, Object.class)); output.setExamples(examples); } // extra keys Set<String> keys = getKeys(node); for (String key : keys) { if (key.startsWith("x-")) { output.setVendorExtension(key, extension(node.get(key))); } else if (!RESPONSE_KEYS.contains(key)) { result.extra(location, key, node.get(key)); } } return output; } public Info info(ObjectNode node, String location, ParseResult result) { if (node == null) return null; Info info = new Info(); String value = getString("title", node, true, location, result); info.title(value); value = getString("description", node, false, location, result); info.description(value); value = getString("termsOfService", node, false, location, result); info.termsOfService(value); ObjectNode obj = getObject("contact", node, false, "contact", result); Contact contact = contact(obj, location, result); info.contact(contact); obj = getObject("license", node, false, location, result); License license = license(obj, location, result); info.license(license); value = getString("version", node, false, location, result); info.version(value); // extra keys Set<String> keys = getKeys(node); for (String key : keys) { if (key.startsWith("x-")) { info.setVendorExtension(key, extension(node.get(key))); } else if (!INFO_KEYS.contains(key)) { result.extra(location, key, node.get(key)); } } return info; } public License license(ObjectNode node, String location, ParseResult result) { if (node == null) return null; License license = new License(); String value = getString("name", node, true, location, result); license.name(value); value = getString("url", node, false, location, result); license.url(value); // extra keys Set<String> keys = getKeys(node); for (String key : keys) { if (key.startsWith("x-")) { license.setVendorExtension(key, extension(node.get(key))); } else if (!LICENSE_KEYS.contains(key)) { result.extra(location + ".license", key, node.get(key)); } } return license; } public Contact contact(ObjectNode node, String location, ParseResult result) { if (node == null) return null; Contact contact = new Contact(); String value = getString("name", node, false, location + ".name", result); contact.name(value); value = getString("url", node, false, location + ".url", result); contact.url(value); value = getString("email", node, false, location + ".email", result); contact.email(value); // extra keys Set<String> keys = getKeys(node); for (String key : keys) { if (!CONTACT_KEYS.contains(key)) { result.extra(location + ".contact", key, node.get(key)); } } return contact; } public Map<String, SecuritySchemeDefinition> securityDefinitions(ObjectNode node, String location, ParseResult result) { if (node == null) return null; Map<String, SecuritySchemeDefinition> output = new HashMap<String, SecuritySchemeDefinition>(); Set<String> keys = getKeys(node); for (String key : keys) { ObjectNode obj = getObject(key, node, false, location, result); SecuritySchemeDefinition def = securityDefinition(obj, location, result); if (def != null) { output.put(key, def); } } return output; } public SecuritySchemeDefinition securityDefinition(ObjectNode node, String location, ParseResult result) { if (node == null) return null; SecuritySchemeDefinition output = null; String type = getString("type", node, true, location, result); if (type != null) { if (type.equals("basic")) { // TODO: parse manually for better feedback output = Json.mapper().convertValue(node, BasicAuthDefinition.class); } else if (type.equals("apiKey")) { String position = getString("in", node, true, location, result); String name = getString("name", node, true, location, result); if (name != null && ("header".equals(position) || "query".equals(position))) { In in = In.forValue(position); if (in != null) { ApiKeyAuthDefinition auth = new ApiKeyAuthDefinition().name(name).in(in); output = auth; } } } else if (type.equals("oauth2")) { // TODO: parse manually for better feedback output = Json.mapper().convertValue(node, OAuth2Definition.class); } else { result.invalidType(location + ".type", "type", "basic|apiKey|oauth2", node); } } return output; } public List<SecurityRequirement> securityRequirements(ArrayNode node, String location, ParseResult result) { if (node == null) return null; List<SecurityRequirement> output = new ArrayList<SecurityRequirement>(); for (JsonNode item : node) { SecurityRequirement security = new SecurityRequirement(); if (item.getNodeType().equals(JsonNodeType.OBJECT)) { ObjectNode on = (ObjectNode) item; Set<String> keys = getKeys(on); for (String key : keys) { List<String> scopes = new ArrayList<>(); ArrayNode obj = getArray(key, on, false, location + ".security", result); if (obj != null) { for (JsonNode n : obj) { if (n.getNodeType().equals(JsonNodeType.STRING)) { scopes.add(n.asText()); } else { result.invalidType(location, key, "string", n); } } } security.requirement(key, scopes); } } output.add(security); } return output; } public List<String> tagStrings(ArrayNode nodes, String location, ParseResult result) { if (nodes == null) return null; List<String> output = new ArrayList<String>(); for (JsonNode node : nodes) { if (node.getNodeType().equals(JsonNodeType.STRING)) { output.add(node.textValue()); } } return output; } public List<Tag> tags(ArrayNode nodes, String location, ParseResult result) { if (nodes == null) return null; List<Tag> output = new ArrayList<Tag>(); for (JsonNode node : nodes) { if (node.getNodeType().equals(JsonNodeType.OBJECT)) { Tag tag = tag((ObjectNode) node, location + ".tags", result); if (tag != null) { output.add(tag); } } } return output; } public Tag tag(ObjectNode node, String location, ParseResult result) { Tag tag = null; if (node != null) { tag = new Tag(); Set<String> keys = getKeys(node); String value = getString("name", node, true, location, result); tag.name(value); value = getString("description", node, false, location, result); tag.description(value); ObjectNode externalDocs = getObject("externalDocs", node, false, location, result); ExternalDocs docs = externalDocs(externalDocs, location + "externalDocs", result); tag.externalDocs(docs); // extra keys for (String key : keys) { if (key.startsWith("x-")) { tag.setVendorExtension(key, extension(node.get(key))); } else if (!TAG_KEYS.contains(key)) { result.extra(location + ".externalDocs", key, node.get(key)); } } } return tag; } public ExternalDocs externalDocs(ObjectNode node, String location, ParseResult result) { ExternalDocs output = null; if (node != null) { output = new ExternalDocs(); Set<String> keys = getKeys(node); String value = getString("description", node, false, location, result); output.description(value); value = getString("url", node, true, location, result); output.url(value); // extra keys for (String key : keys) { if (key.startsWith("x-")) { output.setVendorExtension(key, extension(node.get(key))); } else if (!EXTERNAL_DOCS_KEYS.contains(key)) { result.extra(location + ".externalDocs", key, node.get(key)); } } } return output; } public String getString(JsonNode node, String location, ParseResult result) { String output = null; if (!node.getNodeType().equals(JsonNodeType.STRING)) { result.invalidType(location, "", "string", node); } else { output = ((TextNode) node).asText(); } return output; } public ArrayNode getArray(String key, ObjectNode node, boolean required, String location, ParseResult result) { JsonNode value = node.get(key); ArrayNode an = null; if (value == null) { if (required) { result.missing(location, key); result.invalid(); } } else if (!value.getNodeType().equals(JsonNodeType.ARRAY)) { result.invalidType(location, key, "array", value); } else { an = (ArrayNode) value; } return an; } public ObjectNode getObject(String key, ObjectNode node, boolean required, String location, ParseResult result) { JsonNode value = node.get(key); ObjectNode on = null; if (value == null) { if (required) { result.missing(location, key); result.invalid(); } } else if (!value.getNodeType().equals(JsonNodeType.OBJECT)) { result.invalidType(location, key, "object", value); if (required) { result.invalid(); } } else { on = (ObjectNode) value; } return on; } public Double getDouble(String key, ObjectNode node, boolean required, String location, ParseResult result) { Double value = null; JsonNode v = node.get(key); if (node == null || v == null) { if (required) { result.missing(location, key); result.invalid(); } } else if (v.getNodeType().equals(JsonNodeType.NUMBER)) { value = v.asDouble(); } return value; } public Number getNumber(String key, ObjectNode node, boolean required, String location, ParseResult result) { Number value = null; JsonNode v = node.get(key); if (node == null || v == null) { if (required) { result.missing(location, key); result.invalid(); } } else if (v.getNodeType().equals(JsonNodeType.NUMBER)) { value = v.numberValue(); } return value; } public Integer getInteger(String key, ObjectNode node, boolean required, String location, ParseResult result) { Integer value = null; JsonNode v = node.get(key); if (node == null || v == null) { if (required) { result.missing(location, key); result.invalid(); } } else if (v.getNodeType().equals(JsonNodeType.NUMBER)) { value = v.intValue(); } return value; } public String getString(String key, ObjectNode node, boolean required, String location, ParseResult result) { String value = null; JsonNode v = node.get(key); if (node == null || v == null) { if (required) { result.missing(location, key); result.invalid(); } } else { value = v.asText(); } return value; } public Set<String> getKeys(ObjectNode node) { Set<String> keys = new LinkedHashSet<>(); if (node == null) { return keys; } Iterator<String> it = node.fieldNames(); while (it.hasNext()) { keys.add(it.next()); } return keys; } static class ParseResult { private boolean valid = true; private Map<Location, JsonNode> extra = new HashMap<Location, JsonNode>(); private Map<Location, String> invalidType = new HashMap<Location, String>(); private List<Location> missing = new ArrayList<Location>(); public void extra(String location, String key, JsonNode value) { extra.put(new Location(location, key), value); } public void missing(String location, String key) { missing.add(new Location(location, key)); } public void invalidType(String location, String key, String expectedType, JsonNode value) { invalidType.put(new Location(location, key), expectedType); } public void invalid() { this.valid = false; } public boolean isValid() { return valid; } public void setValid(boolean valid) { this.valid = valid; } public Map<Location, JsonNode> getExtra() { return extra; } public void setExtra(Map<Location, JsonNode> extra) { this.extra = extra; } public Map<Location, String> getInvalidType() { return invalidType; } public void setInvalidType(Map<Location, String> invalidType) { this.invalidType = invalidType; } public List<Location> getMissing() { return missing; } public void setMissing(List<Location> missing) { this.missing = missing; } public List<String> getMessages() { List<String> messages = new ArrayList<String>(); for (Location l : extra.keySet()) { String location = l.location.equals("") ? "" : l.location + "."; String message = "attribute " + location + l.key + " is unexpected"; messages.add(message); } for (Location l : invalidType.keySet()) { String location = l.location.equals("") ? "" : l.location + "."; String message = "attribute " + location + l.key + " is not of type `" + invalidType.get(l) + "`"; messages.add(message); } for (Location l : missing) { String location = l.location.equals("") ? "" : l.location + "."; String message = "attribute " + location + l.key + " is missing"; messages.add(message); } return messages; } } static class Location { private String location; @Override public boolean equals(Object o) { if (this == o) return true; if (!(o instanceof Location)) return false; Location location1 = (Location) o; if (location != null ? !location.equals(location1.location) : location1.location != null) return false; return !(key != null ? !key.equals(location1.key) : location1.key != null); } @Override public int hashCode() { int result = location != null ? location.hashCode() : 0; result = 31 * result + (key != null ? key.hashCode() : 0); return result; } private String key; public Location(String location, String key) { this.location = location; this.key = key; } } }