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));
        }
      }
    } else {
      result.invalidType("", "", "object", node);
      result.invalid();
      return null;
    }
    return swagger;
  }
  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;
  }
  /** Builds the document header of the swagger model */
  private void overview() {
    Info info = swagger.getInfo();
    this.markupDocBuilder.documentTitle(info.getTitle());
    this.markupDocBuilder.sectionTitleLevel1(OVERVIEW);
    if (isNotBlank(info.getDescription())) {
      this.markupDocBuilder.textLine(info.getDescription());
    }
    if (isNotBlank(info.getVersion())) {
      this.markupDocBuilder.sectionTitleLevel2(CURRENT_VERSION);
      this.markupDocBuilder.textLine(VERSION + info.getVersion());
    }
    Contact contact = info.getContact();
    if (contact != null) {
      this.markupDocBuilder.sectionTitleLevel2(CONTACT_INFORMATION);
      if (isNotBlank(contact.getName())) {
        this.markupDocBuilder.textLine(CONTACT_NAME + contact.getName());
      }
      if (isNotBlank(contact.getEmail())) {
        this.markupDocBuilder.textLine(CONTACT_EMAIL + contact.getEmail());
      }
    }

    License license = info.getLicense();
    if (license != null && (isNotBlank(license.getName()) || isNotBlank(license.getUrl()))) {
      this.markupDocBuilder.sectionTitleLevel2(LICENSE_INFORMATION);
      if (isNotBlank(license.getName())) {
        this.markupDocBuilder.textLine(LICENSE + license.getName());
      }
      if (isNotBlank(license.getUrl())) {
        this.markupDocBuilder.textLine(LICENSE_URL + license.getUrl());
      }
    }
    if (isNotBlank(info.getTermsOfService())) {
      this.markupDocBuilder.textLine(TERMS_OF_SERVICE + info.getTermsOfService());
    }

    if (isNotBlank(swagger.getHost())
        || isNotBlank(swagger.getBasePath())
        || isNotEmpty(swagger.getSchemes())) {
      this.markupDocBuilder.sectionTitleLevel2(URI_SCHEME);
      if (isNotBlank(swagger.getHost())) {
        this.markupDocBuilder.textLine(HOST + swagger.getHost());
      }
      if (isNotBlank(swagger.getBasePath())) {
        this.markupDocBuilder.textLine(BASE_PATH + swagger.getBasePath());
      }
      if (isNotEmpty(swagger.getSchemes())) {
        List<String> schemes = new ArrayList<>();
        for (Scheme scheme : swagger.getSchemes()) {
          schemes.add(scheme.toString());
        }
        this.markupDocBuilder.textLine(SCHEMES + join(schemes, ", "));
      }
    }

    if (isNotEmpty(swagger.getTags())) {
      this.markupDocBuilder.sectionTitleLevel2(TAGS);
      List<String> tags = new ArrayList<>();
      for (Tag tag : swagger.getTags()) {
        String name = tag.getName();
        String description = tag.getDescription();
        if (isNoneBlank(description)) {
          tags.add(name + ": " + description);
        } else {
          tags.add(name);
        }
      }
      this.markupDocBuilder.unorderedList(tags);
    }

    if (isNotEmpty(swagger.getConsumes())) {
      this.markupDocBuilder.sectionTitleLevel2(CONSUMES);
      this.markupDocBuilder.unorderedList(swagger.getConsumes());
    }

    if (isNotEmpty(swagger.getProduces())) {
      this.markupDocBuilder.sectionTitleLevel2(PRODUCES);
      this.markupDocBuilder.unorderedList(swagger.getProduces());
    }
  }