reinert / JJSchema

A generator from Java Types to JSON-Schema using Jackson.
Other
117 stars 56 forks source link

processPropertyCollection fails with ClassCastException on nested generics #37

Closed yasammez closed 6 years ago

yasammez commented 9 years ago

File: JsonSchemaGenerator.java Method: processPropertyCollection Line: 205

How to reproduce: have a remote transfer object with nested generics, like List<Optional> or the like and call generateSchema with it. Expected result: a schema describing the input object. Actual result: ClassCastException. Reason: The method doesn't resolve the generic recursive, but only one level deep.

reinert commented 9 years ago

Thanks for reporting this issue @fischmax. Could you try using the v1 package and check if you get the same error? JsonSchemaGenerator is deprecated. To use the v1 package you have to checkout the project to your local machine and install it. Let me know if you have any problem. Thanks!

yasammez commented 9 years ago

Thanks for your fast reply, it is much appreciated!

I just tested with v1 and still get the same error. For a minimal (non-)working example, add

    private List<Optional<URI>> uris;

to the example Product class. I get the following stack trace:

Exception in thread "main" java.lang.ClassCastException: sun.reflect.generics.reflectiveObjects.ParameterizedTypeImpl cannot be cast to java.lang.Class at com.github.reinert.jjschema.v1.PropertyWrapper.(PropertyWrapper.java:77) at com.github.reinert.jjschema.v1.CustomSchemaWrapper.processProperties(CustomSchemaWrapper.java:134) at com.github.reinert.jjschema.v1.CustomSchemaWrapper.(CustomSchemaWrapper.java:60) at com.github.reinert.jjschema.v1.CustomSchemaWrapper.(CustomSchemaWrapper.java:47) at com.github.reinert.jjschema.v1.CustomSchemaWrapper.(CustomSchemaWrapper.java:43) at com.github.reinert.jjschema.v1.SchemaWrapperFactory.createWrapper(SchemaWrapperFactory.java:69) at com.github.reinert.jjschema.v1.SchemaWrapperFactory.createWrapper(SchemaWrapperFactory.java:45) at com.github.reinert.jjschema.v1.SchemaWrapperFactory.createWrapper(SchemaWrapperFactory.java:37) at com.github.reinert.jjschema.v1.JsonSchemaV4Factory.createSchema(JsonSchemaV4Factory.java:36) at Entry.main(Entry.java:9)

reinert commented 9 years ago

I checked the issue. The solution is not trivial...

The issue isn't (only) that the code doesn't handle recursive generics.

It's indeed that the code (1) only handles generics of collections - it should handle generics of any class - and (2) doesn't handle recursive generics.

You can check it in PropertyWrapper class (L73-79).

Unfortunately I do not have the available time to solve it. I encourage you to go deep in this issue and fix it.

Contributions are always welcome!

yasammez commented 9 years ago

At my workplace, the decision was made to deploy a self-written schema generator instead, which had the same problem which I fixed in a rather ad-hoc way. I am pretty sure I am not allowed to dump the whole thing here, but the important part is:

        while (subtype instanceof ParameterizedType) {
            if (Iterable.class.isAssignableFrom((Class<?>) ((ParameterizedType) subtype)
                    .getRawType()))
                arrayDepth = arrayDepth + 1;
            subtype = ((ParameterizedType) subtype).getActualTypeArguments()[0];
        }

As you can see, this merely strips all other generics of the type (we have to deal mostly with wrappers like Optional<> which are not important to us in the JSON) and tracks an additional arrayDepth field which is later used to wrap the thing in

{
  'type':'array',
  'items': { … }
}

blocks.

Recursion on the other hand is handled by tracking a list of visited types and inserting a { '$ref': <ID> } at the appropriate point.

I will ask my employer if I may share the whole thing, depending on if you are even interested: the solution for the generics is probably only useful for us and a more general approach would be nice. But then, you never now what someone is going to do with generics, so it could be hard to anticipate everything.

reinert commented 9 years ago

I see. Recursion is already solved this way in JJSchema. Nested generics, however, is not handled. I appreciate your tip. Thanks.