mbknor / mbknor-jackson-jsonSchema

Generate JSON Schema with Polymorphism using Jackson annotations
MIT License
235 stars 79 forks source link

Add support for generic types with varying type arguments #94

Closed luqasn closed 5 years ago

luqasn commented 5 years ago

In order to generate something like the following:

{
  "$schema" : "http://json-schema.org/draft-04/schema#",
  "title" : "Generic Class Container",
  "type" : "object",
  "additionalProperties" : false,
  "properties" : {
    "child1" : {
      "$ref" : "#/definitions/GenericClass[String]"
    },
    "child2" : {
      "$ref" : "#/definitions/GenericClass[BoringClass]"
    },
    "child3" : {
      "$ref" : "#/definitions/GenericClassTwo[String,GenericClass[BoringClass]]"
    }
  },
  "definitions" : {
    "GenericClass[String]" : {
      "type" : "object",
      "additionalProperties" : false,
      "properties" : {
        "data" : {
          "type" : "string"
        }
      }
    },
    "GenericClass[BoringClass]" : {
      "type" : "object",
      "additionalProperties" : false,
      "properties" : {
        "data" : {
          "$ref" : "#/definitions/BoringClass"
        }
      }
    },
    "BoringClass" : {
      "type" : "object",
      "additionalProperties" : false,
      "properties" : {
        "data" : {
          "type" : "integer"
        }
      },
      "required" : [ "data" ]
    },
    "GenericClassTwo[String,GenericClass[BoringClass]]" : {
      "type" : "object",
      "additionalProperties" : false,
      "properties" : {
        "data1" : {
          "type" : "string"
        },
        "data2" : {
          "$ref" : "#/definitions/GenericClass[BoringClass]"
        }
      }
    }
  }
}

from

public class GenericClassContainer {
    public GenericClass<String> child1 = new GenericClass<>("asdf");
    public GenericClass<BoringClass> child2 = new GenericClass<>(new BoringClass(1337));
    public GenericClassTwo<String, GenericClass<BoringClass>> child3 =
            new GenericClassTwo<>("qqq", new GenericClass<>(new BoringClass(1337)));

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        GenericClassContainer that = (GenericClassContainer) o;
        return Objects.equals(child1, that.child1) &&
                Objects.equals(child2, that.child2) &&
                Objects.equals(child3, that.child3);
    }

    @Override
    public int hashCode() {
        return Objects.hash(child1, child2, child3);
    }
}
luqasn commented 5 years ago

Just for more context: the example I posted (that is also in the tests) generates a wrong schema in current master and thus fails validation:

{
  "$schema" : "http://json-schema.org/draft-04/schema#",
  "title" : "Generic Class Container",
  "type" : "object",
  "additionalProperties" : false,
  "properties" : {
    "child1" : {
      "$ref" : "#/definitions/GenericClass"
    },
    "child2" : {
      "$ref" : "#/definitions/GenericClass"
    },
    "child3" : {
      "$ref" : "#/definitions/GenericClassTwo"
    }
  },
  "definitions" : {
    "GenericClass" : {
      "type" : "object",
      "additionalProperties" : false,
      "properties" : {
        "data" : {
          "type" : "string"
        }
      }
    },
    "GenericClassTwo" : {
      "type" : "object",
      "additionalProperties" : false,
      "properties" : {
        "data1" : {
          "type" : "string"
        },
        "data2" : {
          "$ref" : "#/definitions/GenericClass"
        }
      }
    }
  }
}
mbknor commented 5 years ago

thanks a lot... manually merged.

worldturner commented 5 years ago

From the examples, the URI's generated for id and $ref (like "#/definitions/GenericClass[BoringClass]") that contain square brackets are invalid.

You could replace them with normal round parenthesis "(" and ")" because those are valid.

If you leave square brackets in the URI's, then the schema's can't be used with validators that stick to the standards.


Long explanation:

According to the JSON schema draft 04, "id" and "$ref" should contains valid URI's. And valid URI's are not allowed to contain square brackets; square brackets must be url-encoded.

The "fragment" section of a URI (the part behind "#") must only contain a "pchar", a slash or a question mark: (RFC3986)

fragment = *( pchar / "/" / "?" )

From appendix A of RFC3986:

pchar = unreserved / pct-encoded / sub-delims / ":" / "@"

pct-encoded = "%" HEXDIG HEXDIG unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~" sub-delims = "!" / "$" / "&" / "'" / "(" / ")" / "*" / "+" / "," / ";" / "="

So the only allowed characters in the fragment are:

https://tools.ietf.org/html/rfc3986#section-3.5 https://tools.ietf.org/html/rfc3986#appendix-A https://stackoverflow.com/questions/40568/are-square-brackets-permitted-in-urls