Using Swagger JAX-RS annotations with generics

4,939 views
Skip to first unread message

Shawn Lauzon

unread,
Mar 2, 2013, 7:54:58 PM3/2/13
to swagger-sw...@googlegroups.com
Hi all,

I'm trying to get Swagger to work with Java Generics; unfortunately when I try to use them, Swagger hangs while parsing the APIs; is this just not supported, and if so, is there a workaround? Alternatively, is this something that is worth trying to solve?

I'd like to do something like this:

    @POST
    @ApiOperation(value = "Create a new graph", responseClass="com.myco.Graph<com.myco.MappedId>")
    @Consumes("application/json")
    public Response postGraph(@ApiParam(value = "The new graph", required = true) Graph<TempId> userGraph)

The graph that comes in is parameterized by a TempId object, and it is returned with a MappedId object (which contains both the "tempId" as well as the generated "id"). I could tell that the responseClass needed to not contain the generic; even when I remove it, I get errors:

java.lang.ClassCastException: sun.reflect.generics.reflectiveObjects.TypeVariableImpl cannot be cast to java.lang.Class
at com.wordnik.swagger.core.ApiPropertiesReader$.getDataType(SpecReader.scala:91)

And if I get rid of the responseClass, then it just hangs, presumably while parsing the @ApiParam. Thoughts, anyone? Thanks in advance.

Best,
shawn. 

tony tam

unread,
Mar 3, 2013, 11:54:13 PM3/3/13
to swagger-sw...@googlegroups.com
Hi Shawn, the return values only support concrete classes, not generics.  I'm not sure if we could introspect it due to type erasure?  If you have thoughts on supporting it, I'd love to hear.

Rune

unread,
Mar 13, 2013, 5:40:36 AM3/13/13
to swagger-sw...@googlegroups.com
Hi and thanks for a cool product!

I have a related problem.

Using Jersey + Spring. 

I'm about to upgrader from Swagger 1.0.1 to 1.2.1 to get rid of extending JavaHelp / custom listing classes.
But after upgrading I get the following errors when listing all api's.  (Swagger "frontpage")

2013-03-13 10:14:56,733 INFO  com.wordnik.swagger.jsonschema.ApiModelParser : Class no.tine.api.jersey.DateTimeParam is not annotated with a @XmlRootElement annotation, using DateTimeParam
2013-03-13 10:14:56,735 INFO  com.wordnik.swagger.jsonschema.ApiModelParser : Class java.util.Date is not annotated with a @XmlRootElement annotation, using Date
2013-03-13 10:14:56,735 INFO  com.wordnik.swagger.jsonschema.ApiModelParser : Class java.util.Date is not annotated with a @XmlRootElement annotation, using Date
2013-03-13 10:14:56,736 ERROR com.wordnik.swagger.jaxrs.HelpApi : Unable to load model documentation for no.tine.api.jersey.DateTimeParam
java.lang.ClassCastException: sun.reflect.generics.reflectiveObjects.TypeVariableImpl cannot be cast to java.lang.Class
at com.wordnik.swagger.core.ApiPropertiesReader$.getDataType(SpecReader.scala:91)
at com.wordnik.swagger.jsonschema.ApiModelParser.parsePropertyAnnotations(SwaggerJsonSchemaProvider.scala:150)


The DateTimeParam class looks like this

public class DateTimeParam extends AbstractParam<DateTime> {

    public DateTimeParam(String param) {
        super(param);
    }

    protected DateTime parse(String param) {
        return DateConverter.convertToDateTime(param);
    }

    public Date getValueAsDate() {
        return getValue() == null ? null : getValue().toDate();
    }

    public static Date getValueAsDate(DateTimeParam dateTimeParam) {
        return dateTimeParam == null ? null : dateTimeParam.getValueAsDate();
    }

}

This class converts String inputs to joda-time DateTime instances for further use in our ReST api.

Examples of use:
 public List<ResultFeedback> historical(@QueryParam("ownerId") Long ownerId,
                                           @ApiParam(value = "YYYY-MM-DD") @QueryParam("fromDate") DateTimeParam fromDate) {

public Response findEvents(
            @PathParam("ownerId") Long ownerId,
            @PathParam("registrationType") RegistrationTypeParam registrationType,
            @ApiParam(required = false) @QueryParam("status") AnimalExt.EnrollmentStatusCombination esc,
            @ApiParam(required = false) @QueryParam("unenrollmentDate") DateTimeParam unenrollmentDate,
            @ApiParam(required = false) @QueryParam("fromDate") DateTimeParam fromDate,
            @HeaderParam("If-Modified-Since") String modified) {

This did not occur when running version 1.0.1 of Swagger.
However, the API listing and the invocation of the the methods all seems to work correctly, but given that this problem is logged as "ERROR", 
I am not willing to push this into production.

This problem occurs for all methods using a parameter whose class is using generics.

public class RegistrationTypeParam extends AbstractParam<Registration.Type> {
public class RegistrationParam extends AbstractJsonParam<String> {
public class ClassParam extends AbstractParam<Class> {



There is also a problem with Enums

2013-03-13 10:14:56,776 ERROR com.wordnik.swagger.jaxrs.HelpApi : Skipping model no.tine.api.internal.v1.core.domain.ext.AnimalExt$EnrollmentStatusCombination. Could not load the model.
2013-03-13 10:14:57,037 ERROR com.wordnik.swagger.jaxrs.HelpApi : Skipping model no.tine.api.internal.v1.i18n.I18n. Could not load the model.
2013-03-13 10:14:57,037 ERROR com.wordnik.swagger.jaxrs.HelpApi : Skipping model no.tine.api.user.domain.Feature$Type. Could not load the model.

Examples
Enum defined in own file

public enum I18n {

    ClawTrimmingNotification("i18n.clawTrimming.clawNotification"),
    ClawTrimmingCodes("i18n.clawTrimming.code"),
    Breed("i18n.breed"),
    UnenrollmentReason("i18n.unenrollment.reason"),
    UnenrollmentCause("i18n.unenrollment.cause"),
    EnrollmentReason("i18n.enrollment.reason"),
    HealthCodes("i18n.healthRecording.healthCodes"),
    HealthRecording("i18n.healthRecording"),
    TreatmentCodes("i18n.healthRecording.treatmentCodes"),
    TreatedBy("i18n.treatedBy");


public class AnimalExt implements Serializable {
.
.
.
.
public static enum EnrollmentStatusCombination implements DomainEnum {
        All,
        AllEnrolled,
        OnlyUnenrolled,
        OnlyAwaitingUnenrollment
    }
.
.
}


This problem is also new after the upgrade. 


I have no Scale experience what so ever, so hopefully you guys can have a look at it.
Is there a bug tracker where I can register this as a bug? 


regards
Rune

tony tam

unread,
Mar 13, 2013, 3:15:00 PM3/13/13
to swagger-sw...@googlegroups.com
Interesting--so it was working fine in 1.0.1?  I'll take a look to see what changed, and see if we can undo whatever it was.  We also have a contributor who has a better reflection technique which would make this problem completely go away, I'll check in on the status of that.

tony tam

unread,
Mar 13, 2013, 3:49:20 PM3/13/13
to swagger-sw...@googlegroups.com
I just tried with 1.0.1 and have the same issue.  Here's the test class:

class GenericObject[T] {
  var t:T = _
  def getValue(): T = t
  def setValue(t:T) = this.t = t
}

and reading the model as such:

ApiPropertiesReader.read(classOf[GenericObject[String]])

gives the error you experienced.  Will look into this more but you should file a ticket (this may be a duplicate) here:

tony tam

unread,
Mar 13, 2013, 4:40:25 PM3/13/13
to swagger-sw...@googlegroups.com
Looks like this is an easy fix.  I'll update github issues shortly with a fix.

Rune

unread,
Mar 14, 2013, 3:11:20 AM3/14/13
to swagger-sw...@googlegroups.com
Cool!
I awaiting your fix!
:)

tony tam

unread,
Mar 14, 2013, 10:32:29 AM3/14/13
to swagger-sw...@googlegroups.com
It's in the latest snapshot (1.2.2-SNAPSHOT) which is available on sonatype oss.  For a simple test case like this:


class GenericObject[T] {
  @BeanProperty var theValue: T = _
}


it should "read a generic" in {
  var docObj = ApiPropertiesReader.read(classOf[GenericObject[String]])
  assert((docObj.getFields.map{_.name}.toSet & Set("theValue")).size === 1)
}

Give it a shot, would love to get it in use.

Rune

unread,
Mar 15, 2013, 5:15:35 AM3/15/13
to swagger-sw...@googlegroups.com
Hi
I now see that I use 

<groupId>com.wordnik</groupId>
<artifactId>swagger-jaxrs_2.9.1</artifactId>

but there is also 
<artifactId>swagger-jersey-jaxrs_2.9.1</artifactId>

I should probably use the last one, but what is the extra value in the jersey-jaxrs artifact?

Rune

unread,
Mar 15, 2013, 5:34:06 AM3/15/13
to swagger-sw...@googlegroups.com

Using

<groupId>com.wordnik</groupId>
<artifactId>swagger-jaxrs_2.9.1</artifactId>
<version>1.2.2-SNAPSHOT</version>

I still get the same error

Version timestamp from oss sonatype repo
1.2.2-SNAPSHOT/Wed Mar 13 19:13:54 CDT 2013

tony tam

unread,
Mar 15, 2013, 10:37:37 AM3/15/13
to swagger-sw...@googlegroups.com
The jersey version gives access to some of the jersey-specific features, like @FormDataParam, which gives you the ability to process file uploads, etc.  Not all server frameworks for JAX-RS actually use Jersey, so we made a separate swagger module.

Can you post your error so I can verify the line numbers?  I'll make sure the right version got deployed to snapshots.  You could also try building from source?

Rune

unread,
Mar 18, 2013, 8:59:18 AM3/18/13
to swagger-sw...@googlegroups.com
Hi

Cloned github and built sources locally to get latest and greatest

I get two different traces

1)

2013-03-18 13:55:41,489 ERROR com.wordnik.swagger.jaxrs.HelpApi : Unable to load model documentation for no.tine.api.jersey.DateTimeParam
java.lang.ClassCastException: sun.reflect.generics.reflectiveObjects.TypeVariableImpl cannot be cast to java.lang.Class
at com.wordnik.swagger.core.ApiPropertiesReader$.getDataType(SpecReader.scala:91)
at com.wordnik.swagger.jsonschema.ApiModelParser.parsePropertyAnnotations(SwaggerJsonSchemaProvider.scala:154)
at com.wordnik.swagger.jsonschema.ApiModelParser.com$wordnik$swagger$jsonschema$ApiModelParser$$parseMethod(SwaggerJsonSchemaProvider.scala:98)
at com.wordnik.swagger.jsonschema.ApiModelParser$$anonfun$parseRecursive$1.apply(SwaggerJsonSchemaProvider.scala:81)
at com.wordnik.swagger.jsonschema.ApiModelParser$$anonfun$parseRecursive$1.apply(SwaggerJsonSchemaProvider.scala:79)

class def
public class DateTimeParam extends AbstractParam<DateTime> {


And 

2)

2013-03-18 13:55:41,500 ERROR com.wordnik.swagger.jaxrs.HelpApi : Unable to load model documentation for no.tine.api.jersey.RegistrationParam
java.lang.ClassCastException: sun.reflect.generics.reflectiveObjects.TypeVariableImpl cannot be cast to java.lang.Class
at com.wordnik.swagger.core.ApiPropertiesReader$.getDataType(SpecReader.scala:84)
at com.wordnik.swagger.core.ApiPropertiesReader$.getDataType(SpecReader.scala:70)
at com.wordnik.swagger.jsonschema.ApiModelParser.parsePropertyAnnotations(SwaggerJsonSchemaProvider.scala:154)
at com.wordnik.swagger.jsonschema.ApiModelParser.com$wordnik$swagger$jsonschema$ApiModelParser$$parseMethod(SwaggerJsonSchemaProvider.scala:98)
at com.wordnik.swagger.jsonschema.ApiModelParser$$anonfun$parseRecursive$1.apply(SwaggerJsonSchemaProvider.scala:81)
at com.wordnik.swagger.jsonschema.ApiModelParser$$anonfun$parseRecursive$1.apply(SwaggerJsonSchemaProvider.scala:79)


class def

tony tam

unread,
Mar 18, 2013, 10:31:11 AM3/18/13
to swagger-sw...@googlegroups.com
Looks like my test coverage isn't good enough.  I'll reopen https://github.com/wordnik/swagger-core/issues/150

tony tam

unread,
Mar 18, 2013, 10:33:06 AM3/18/13
to swagger-sw...@googlegroups.com
make that this issue, not #150:

Justin Robbins

unread,
Apr 25, 2013, 9:17:33 AM4/25/13
to swagger-sw...@googlegroups.com
Hello swagger folks,

I'm working hard to swagger enable my API (many thanks for your CXF example).

Can swagger work with generics?  After skimming this thread is looks like the answer was at first No but was work subsequently done to enable swagger to work with Java generics?

If it helps, here's a link to an example abstract class which my resources extend:
https://github.com/justinhrobbins/FlashCards_App/blob/545323db69b864cc11abdcb3592b5437af039347/FlashCards_WebServices/src/main/java/org/robbins/flashcards/webservices/base/AbstractGenericResource.java#L19

You can see I am using generics as both parameters and responses.

Here's a link to a class that extends the abstract class:
https://github.com/justinhrobbins/FlashCards_App/blob/352b674de0818d26e93fddd3cc61eb07e7bcd71d/FlashCards_WebServices/src/main/java/org/robbins/flashcards/webservices/TagsResource.java

tony tam

unread,
Apr 25, 2013, 9:25:26 AM4/25/13
to swagger-sw...@googlegroups.com
Hi, it can work with Generics but if you want proper representation of models you'll have to do some manual mapping of your response types to swagger model definitions.  See here:


As for your sample, I can tell you that putting the javax.ws.core.Response as the return type is probably not what you want to do--this will tell swagger that the properties of the Response object should be exposed, not the actual model that you're returning.  If this is a PUT request (i.e. no response body), you should make that "void".

What other issues are you seeing?

Justin Robbins

unread,
Apr 25, 2013, 11:28:03 AM4/25/13
to swagger-sw...@googlegroups.com
many thanks for your quick response

1.) <quote>if you want proper representation of models you'll have to do some manual mapping of your response types to swagger model definitions</quote>
Unfortunately, I'm not yet familiar with Scala.  By chance are there any JAX-RS examples that use Java generics (and swagger annotations) that you could point me to?  I'm not sure how to best manually map my response to swagger model defs.

2.) <quote>I can tell you that putting the javax.ws.core.Response as the return type is probably not what you want to do</quote>
Isn't using Response as the return type fairly common for @Put and @Delete methods?  For example, the CXF docs show exactly that.  http://cxf.apache.org/docs/jax-rs-basics.html  Are you saying it's bad practice for any REST API or just it doesn't play well with swagger?

3.) <quote>What other issues are you seeing?</quote>
It took me a while to get swagger to play with CXF config.  Now that I'm over the hump, I've just started adding swagger annotations to my API.  I'm sure I'll have more questions going forward.

Again, many thanks for your great tool, your CXF sample and taking the time to provide support to developers trying to make their API's swagger!

Abderrazak BOUADMA

unread,
Apr 25, 2013, 11:43:31 AM4/25/13
to swagger-sw...@googlegroups.com
Hi Shawn,

did yo tried to make a call with another tool (REST Console on Chrome or fiddler) ? is responsing ?
the Exception seems to me normal due to type erasure in java 

hop that helped you a little bit

tony tam

unread,
Apr 25, 2013, 4:16:31 PM4/25/13
to swagger-sw...@googlegroups.com
Howdy,

1) yes, since the issue of generics is coming up a lot, I'll see about getting a java-based sample included shortly.  There are two scenarios to handle-- one where the resource itself is generic, the other where the response classes are.

2) you can continue to set the Java response as javax.ws.core.Response, but tell swagger it's "void" if there is no actual response payload.

3) sounds great, just post things as you see them.

I'll post here when the sample is up.

Wesley Acheson

unread,
Jan 14, 2014, 11:27:03 AM1/14/14
to swagger-sw...@googlegroups.com
Was a sample ever posted of this?

I can see this and in the ChangeLog support for generics has been added but I can't find out how its been implemented.
Reply all
Reply to author
Forward
0 new messages