Skip to content

Problem with Model containing an Array property #14

Closed

Description

I came across this while testing my api resourses.json with the validator

./bin/validate.sh http://localhost/restler3.dev/raw/swagger/resources.json "arul" ./swagger-errors.html

is throwing the following exception when my Author model contains a property called books of type Array

api.path: /resources/authors.{format}
calling: http://localhost/restler3.dev/raw/swagger/resources/authors.json?api_key=arul
swagger version: 1.1
basePath: http://localhost/restler3.dev/raw/swagger
api version: 1
java.lang.NullPointerException
    at com.wordnik.swagger.codegen.util.CoreUtils$$anonfun$extractModels$4$$anonfun$apply$11.apply(CoreUtils.scala:138)
    at com.wordnik.swagger.codegen.util.CoreUtils$$anonfun$extractModels$4$$anonfun$apply$11.apply(CoreUtils.scala:135)
    at scala.collection.Iterator$class.foreach(Iterator.scala:660)
    at scala.collection.JavaConversions$JMapWrapperLike$$anon$2.foreach(JavaConversions.scala:781)
    at scala.collection.IterableLike$class.foreach(IterableLike.scala:73)
    at scala.collection.JavaConversions$JMapWrapper.foreach(JavaConversions.scala:792)
    at com.wordnik.swagger.codegen.util.CoreUtils$$anonfun$extractModels$4.apply(CoreUtils.scala:135)
    at com.wordnik.swagger.codegen.util.CoreUtils$$anonfun$extractModels$4.apply(CoreUtils.scala:134)
    at scala.collection.TraversableLike$$anonfun$map$1.apply(TraversableLike.scala:194)
    at scala.collection.TraversableLike$$anonfun$map$1.apply(TraversableLike.scala:194)
    at scala.collection.mutable.HashMap$$anonfun$foreach$1.apply(HashMap.scala:93)
    at scala.collection.mutable.HashMap$$anonfun$foreach$1.apply(HashMap.scala:93)
    at scala.collection.Iterator$class.foreach(Iterator.scala:660)
    at scala.collection.mutable.HashTable$$anon$1.foreach(HashTable.scala:157)
    at scala.collection.mutable.HashTable$class.foreachEntry(HashTable.scala:190)
    at scala.collection.mutable.HashMap.foreachEntry(HashMap.scala:43)
    at scala.collection.mutable.HashMap.foreach(HashMap.scala:93)
    at scala.collection.TraversableLike$class.map(TraversableLike.scala:194)
    at scala.collection.mutable.HashMap.map(HashMap.scala:43)
    at com.wordnik.swagger.codegen.util.CoreUtils$.extractModels(CoreUtils.scala:134)
    at com.wordnik.swagger.codegen.util.CoreUtils$$anonfun$extractAllModels$1.apply(CoreUtils.scala:45)
    at com.wordnik.swagger.codegen.util.CoreUtils$$anonfun$extractAllModels$1.apply(CoreUtils.scala:44)
    at scala.collection.LinearSeqOptimized$class.foreach(LinearSeqOptimized.scala:59)
    at scala.collection.immutable.List.foreach(List.scala:45)
    at com.wordnik.swagger.codegen.util.CoreUtils$.extractAllModels(CoreUtils.scala:44)
    at com.wordnik.swagger.codegen.spec.SwaggerSpecValidator.validateResponseModels(SwaggerSpecValidator.scala:63)
    at com.wordnik.swagger.codegen.spec.SwaggerSpecValidator.validate(SwaggerSpecValidator.scala:57)
    at com.wordnik.swagger.codegen.spec.Validator$.main(Validator.scala:54)
    at com.wordnik.swagger.codegen.spec.Validator.main(Validator.scala)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
    at java.lang.reflect.Method.invoke(Method.java:597)
    at scala.tools.nsc.util.ScalaClassLoader$$anonfun$run$1.apply(ScalaClassLoader.scala:78)
    at scala.tools.nsc.util.ScalaClassLoader$class.asContext(ScalaClassLoader.scala:24)
    at scala.tools.nsc.util.ScalaClassLoader$URLClassLoader.asContext(ScalaClassLoader.scala:88)
    at scala.tools.nsc.util.ScalaClassLoader$class.run(ScalaClassLoader.scala:78)
    at scala.tools.nsc.util.ScalaClassLoader$URLClassLoader.run(ScalaClassLoader.scala:101)
    at scala.tools.nsc.ObjectRunner$.run(ObjectRunner.scala:33)
    at scala.tools.nsc.ObjectRunner$.runAndCatch(ObjectRunner.scala:40)
    at scala.tools.nsc.MainGenericRunner.runTarget$1(MainGenericRunner.scala:56)
    at scala.tools.nsc.MainGenericRunner.process(MainGenericRunner.scala:80)
    at scala.tools.nsc.MainGenericRunner$.main(MainGenericRunner.scala:89)
    at scala.tools.nsc.MainGenericRunner.main(MainGenericRunner.scala)

Here is the /resources/authors.json for your reference

{
  "apiVersion": "1",
  "swaggerVersion": "1.1",
  "basePath": "http://localhost/restler3.dev/raw/swagger",
  "apis": [
    {
      "path": "/authors.{format}/{id}",
      "description": "API to list, modify Authors of the Restler",
      "operations": [
        {
          "httpMethod": "GET",
          "nickname": "authorsid",
          "responseClass": "Author",
          "parameters": [
            {
              "name": "id",
              "description": "Author ID . ",
              "paramType": "path",
              "required": true,
              "allowMultiple": false,
              "dataType": "string"
            }
          ],
          "summary": "Get Author for the specified Author ID",
          "notes": "Passing a valid author id, will result in getting details (name, email, author id) of the specific Author.",
          "errorResponses": [
            {
              "reason": "one or more of required fields missing",
              "code": 417
            },
            {
              "reason": "Author not found",
              "code": 404
            }
          ]
        }
      ]
    },
    {
      "path": "/authors.{format}",
      "description": "API to list, modify Authors of the Restler",
      "operations": [
        {
          "httpMethod": "POST",
          "nickname": "authors",
          "responseClass": "Array",
          "parameters": [
            {
              "name": "request_data",
              "description": "",
              "paramType": "path",
              "required": true,
              "allowMultiple": false,
              "dataType": "string"
            }
          ],
          "summary": "Create new Author",
          "notes": "",
          "errorResponses": [
            {
              "reason": "one or more of required fields missing",
              "code": 417
            }
          ]
        }
      ]
    },
    {
      "path": "/authors.{format}/{id}",
      "description": "API to list, modify Authors of the Restler",
      "operations": [
        {
          "httpMethod": "PUT",
          "nickname": "authorsid",
          "responseClass": "Array",
          "parameters": [
            {
              "name": "id",
              "description": "Author ID . ",
              "paramType": "path",
              "required": true,
              "allowMultiple": false,
              "dataType": "string"
            },
            {
              "name": "request_data",
              "description": "",
              "paramType": "path",
              "required": true,
              "allowMultiple": false,
              "dataType": "string"
            }
          ],
          "summary": "Update Author information",
          "notes": "",
          "errorResponses": [

          ]
        }
      ]
    },
    {
      "path": "/authors.{format}/something/{id}",
      "description": "API to list, modify Authors of the Restler",
      "operations": [
        {
          "httpMethod": "DELETE",
          "nickname": "authorssomethingid",
          "responseClass": "Array",
          "parameters": [
            {
              "name": "id",
              "description": "Author ID . ",
              "paramType": "path",
              "required": true,
              "allowMultiple": false,
              "dataType": "string"
            }
          ],
          "summary": "Delete Author by ID",
          "notes": "",
          "errorResponses": [

          ]
        }
      ]
    }
  ],
  "resourcePath": "/authors",
  "models": {
    "Author": {
      "id": "Author",
      "properties": {
        "name": {
          "type": "string"
        },
        "email": {
          "type": "string"
        },
        "age": {
          "type": "int"
        },
        "books": {
          "type": "Array"
        }
      }
    }
  }
}

if I remove , "books":{ "type":"Array"} validator is fine

Activity

  1. fehguy commented on Aug 28, 2012

    @fehguy
    Contributor

    Looks lik your array doesn't specify the type of object in it. If made a string, you'll get a valid spec:

      "models": {
        "Author": {
          "id": "Author",
          "properties": {
            "name": {
              "type": "string"
            },
            "email": {
              "type": "string"
            },
            "age": {
              "type": "int"
            },
            "books": {
              "type": "Array",
              "items":{
                "type":"string"
              }
            }
          }
        }
      }
    

    Will fix the validation step so you get a proper error.

  2. fehguy commented on Aug 28, 2012

    @fehguy
    Contributor

    OK pushed a fix to do a null check on the item if it's an array. I found three other issues in your model, namely:

    • the DELETE method must return void
    • the PUT method must return void
    • the POST method is returning an Array[Author] not Array

    The entire authors.json file is here:

    {
      "apiVersion": "1",
      "swaggerVersion": "1.1",
      "basePath": "http://localhost/restler3.dev/raw/swagger",
      "apis": [
        {
          "path": "/authors.{format}/{id}",
          "description": "API to list, modify Authors of the Restler",
          "operations": [
            {
              "httpMethod": "GET",
              "nickname": "authorsid",
              "responseClass": "Author",
              "parameters": [
                {
                  "name": "id",
                  "description": "Author ID . ",
                  "paramType": "path",
                  "required": true,
                  "allowMultiple": false,
                  "dataType": "string"
                }
              ],
              "summary": "Get Author for the specified Author ID",
              "notes": "Passing a valid author id, will result in getting details (name, email, author id) of the specific Author.",
              "errorResponses": [
                {
                  "reason": "one or more of required fields missing",
                  "code": 417
                },
                {
                  "reason": "Author not found",
                  "code": 404
                }
              ]
            }
          ]
        },
        {
          "path": "/authors.{format}",
          "description": "API to list, modify Authors of the Restler",
          "operations": [
            {
              "httpMethod": "POST",
              "nickname": "authors",
              "responseClass": "Array[Author]",
              "parameters": [
                {
                  "name": "request_data",
                  "description": "",
                  "paramType": "path",
                  "required": true,
                  "allowMultiple": false,
                  "dataType": "string"
                }
              ],
              "summary": "Create new Author",
              "notes": "",
              "errorResponses": [
                {
                  "reason": "one or more of required fields missing",
                  "code": 417
                }
              ]
            }
          ]
        },
        {
          "path": "/authors.{format}/{id}",
          "description": "API to list, modify Authors of the Restler",
          "operations": [
            {
              "httpMethod": "PUT",
              "nickname": "authorsid",
              "responseClass": "void",
              "parameters": [
                {
                  "name": "id",
                  "description": "Author ID . ",
                  "paramType": "path",
                  "required": true,
                  "allowMultiple": false,
                  "dataType": "string"
                },
                {
                  "name": "request_data",
                  "description": "",
                  "paramType": "path",
                  "required": true,
                  "allowMultiple": false,
                  "dataType": "string"
                }
              ],
              "summary": "Update Author information",
              "notes": "",
              "errorResponses": [
    
              ]
            }
          ]
        },
        {
          "path": "/authors.{format}/something/{id}",
          "description": "API to list, modify Authors of the Restler",
          "operations": [
            {
              "httpMethod": "DELETE",
              "nickname": "authorssomethingid",
              "responseClass": "void",
              "parameters": [
                {
                  "name": "id",
                  "description": "Author ID . ",
                  "paramType": "path",
                  "required": true,
                  "allowMultiple": false,
                  "dataType": "string"
                }
              ],
              "summary": "Delete Author by ID",
              "notes": "",
              "errorResponses": [
    
              ]
            }
          ]
        }
      ],
      "resourcePath": "/authors",
      "models": {
        "Author": {
          "id": "Author",
          "properties": {
            "name": {
              "type": "string"
            },
            "email": {
              "type": "string"
            },
            "age": {
              "type": "int"
            },
            "books": {
              "items":{
                "type":"string"
              },
              "type": "Array"
            }
          }
        }
      }
    }

    Thanks!

  3. fehguy commented on Aug 29, 2012

    @fehguy
    Contributor

    sweet!

  4. venkata-denali commented on Jun 6, 2013

    @venkata-denali

    Hi, I have a problem with model schema. It is not able to display the type for embedded class. For suppose, In one response body class I have is user defined class and it has List like objects. In swagger model schema it presents it as Array[any]. Is there is way specify the exact type.

  5. fehguy commented on Jun 6, 2013

    @fehguy
    Contributor

    Can you share the class itself that you're trying to represent? What version of codegen? Is it java or scala code?

  6. venkata-denali commented on Jun 6, 2013

    @venkata-denali

    I am using code generation for java. This is my pojo class.

    @XmlRootElement(name="SubscriptionDetails")
    public class SubscriptionDetails implements Serializable{

    public static enum PROTOCOL {SMS,EMAIL,FAX};
    
    
    
    private UserDetails subscriber;
    
    private List<SubscriptionCriteria> subscriptionCriterias;
    
    private TimingDetails alertTimings;
    
    private Boolean active;
    
    private List<PROTOCOL> protocols = new ArrayList<SubscriptionDetails.PROTOCOL>();
    
    private List<NotificationPreference> notificationPreferences;
    
    
    
    /**
     * @param alertTypeId the alertTypeId to set
     */
    public void setAlertTypeId(Integer alertTypeId) {
        this.alertTypeId = alertTypeId;
    
    }
    
    /**
     * @return the alertTypeId
     */
    public Integer getAlertTypeId() {
        return alertTypeId;
    }
    
    
    /**
     * @param subscriptionId the subscriptionId to set
     */
    public void setSubscriptionId(Integer subscriptionId) {
        this.subscriptionId = subscriptionId;
    
    }
    
    /**
     * @return the subscriptionId
     */
    public Integer getSubscriptionId() {
        return subscriptionId;
    }
    
    /**
     * @param active the active to set
     */
    public void setActive(Boolean active) {
        this.active = active;
    
    }
    
    /**
     * @return the active
     */
    public Boolean getActive() {
        return active;
    }
    
    /**
     * @param protocols the protocols to set
     */
    public void setProtocols(List<PROTOCOL> protocols) {
        this.protocols = protocols;
    
    }
    
    /**
     * @return the protocols
     */
    public List<PROTOCOL> getProtocols() {
        return protocols;
    }
    
    /**
     * @param alertTimings the alertTimings to set
     */
    public void setAlertTimings(TimingDetails alertTimings) {
        this.alertTimings = alertTimings;
    }
    
    /**
     * @return the alertTimings
     */
    public TimingDetails getAlertTimings() {
        return alertTimings;
    }
    
    
    
    /**
     * @return the notificationPreferences
     */
    public List<NotificationPreference> getNotificationPreferences() {
        return notificationPreferences;
    }
    
    /**
     * @param notificationPreferences the notificationPreferences to set
     */
    public void setNotificationPreferences(
            List<NotificationPreference> notificationPreferences) {
        this.notificationPreferences = notificationPreferences;
    }
    
    
    
    /**
     * @param criteria the criteria to set
     */
    public void setCriteria(String criteria) {
        this.criteria = criteria;
    }
    
    /**
     * @return the criteria
     */
    public String getCriteria() {
        return criteria;
    }
    
    /**
     * @param emailIds the emailIds to set
     */
    public void setEmailIds(String emailIds) {
        this.emailIds = emailIds;
    }
    
    /**
     * @return the emailIds
     */
    public String getEmailIds() {
        return emailIds;
    }
    
    /**
     * @param mobileNos the mobileNos to set
     */
    public void setMobileNos(String mobileNos) {
        this.mobileNos = mobileNos;
    }
    
    /**
     * @return the mobileNos
     */
    public String getMobileNos() {
        return mobileNos;
    }
    
    /**
     * @param alertType the alertType to set
     */
    public void setAlertType(String alertType) {
        this.alertType = alertType;
    }
    
    /**
     * @return the alertType
     */
    public String getAlertType() {
        return alertType;
    }
    
    /**
     * @param createdDate the createdDate to set
     */
    public void setCreatedDate(Date createdDate) {
        this.createdDate = createdDate;
    }
    
    /**
     * @return the createdDate
     */
    public Date getCreatedDate() {
        return createdDate;
    }
    
    /**
     * @param updatedDate the updatedDate to set
     */
    public void setUpdatedDate(Date updatedDate) {
        this.updatedDate = updatedDate;
    }
    
    /**
     * @return the updatedDate
     */
    public Date getUpdatedDate() {
        return updatedDate;
    }
    
    /**
     * @return the protocolTypes
     */
    public String getProtocolTypes() {
        return protocolTypes;
    }
    
    /**
     * @param protocolTypes the protocolTypes to set
     */
    public void setProtocolTypes(String protocolTypes) {
        this.protocolTypes = protocolTypes;
    }
    
    /**
     * @param subscriptionCriterias the subscriptionCriterias to set
     */
    public void setSubscriptionCriterias(List<SubscriptionCriteria> subscriptionCriterias) {
        this.subscriptionCriterias = subscriptionCriterias;
    }
    
    /**
     * @return the subscriptionCriterias
     */
    public List<SubscriptionCriteria> getSubscriptionCriterias() {
        return subscriptionCriterias;
    }
    
    /**
     * @param subscriber the subscriber to set
     */
    public void setSubscriber(UserDetails subscriber) {
        this.subscriber = subscriber;
    }
    
    /**
     * @return the subscriber
     */
    public UserDetails getSubscriber() {
        return subscriber;
    }
    

    }

    The web service class is

    @OverRide
    @put
    @path("/subscriptions")
    @consumes({"application/json","application/xml"})
    @produces({"application/json","application/xml"})
    @apioperation(value = "Updates an existing subscription", notes = "Updates the subscription details")
    @ApiErrors(value = { @ApiError(code = 400, reason = "Subscription details are required") })
    public Response updateSubscription(@ApiParam(value = "SubscriptionDetails that needs to be fetched", required = true) SubscriptionDetails subscription) throws DataRequiredException{
    //Code
    }

  7. venkata-denali commented on Jun 6, 2013

    @venkata-denali

    This is model schema was generated by swagger ui

    SubscriptionDetails
    class SubscriptionDetails(alertTypeId: int, criteria: string, updatedDate: Date, emailIds: string, protocols: Array[any], alertType: string, subscriptionId: int, subscriptionCriterias: Array[any], mobileNos: string, active: boolean, alertTimings: TimingDetails, notificationPreferences: Array[any], subscriber: UserDetails, createdDate: Date, protocolTypes: string)

    Please note Array[any] notation for list typed property.

  8. fehguy commented on Jun 6, 2013

    @fehguy
    Contributor

    Can you confirm you're using swagger 1.2.4?

  9. venkata-denali commented on Jun 6, 2013

    @venkata-denali

    The maven repository for latest swagger-core_2.9.1 is 1.2.3.

    This my dependency set

    com.wordnik swagger-core_2.9.1 1.2.3 com.wordnik swagger-jaxrs_2.9.1 1.2.3
    <dependency>
     <groupId>org.reflections</groupId>
     <artifactId>reflections</artifactId>
     <version>0.9.8</version>
    
    <dependency>
     <groupId>org.scala-lang</groupId>
     <artifactId>scala-library</artifactId>
     <version>2.9.1-1</version>
    
    org.scalatest scalatest_2.9.1 1.6.1 test org.slf4j log4j-over-slf4j 1.6.3 runtime org.slf4j jcl-over-slf4j 1.6.3 org.slf4j jul-to-slf4j 1.6.3 ch.qos.logback logback-classic 1.0.9 runtime
  10. venkata-denali commented on Jun 6, 2013

    @venkata-denali

    But 1.2.4 is not available

  11. fehguy commented on Jun 6, 2013

    @fehguy
    Contributor

    Hi, there are a number of models missing from your sample, and not all the class variables are there, so without reproducing all of it, it's impossible to test.

    I just ensured 1.2.4 was pushed to maven central, so if you can try that again shortly, or try 1.2.4-SNAPSHOT (in sonatype oss snapshots) I'd like to see if the issue is fixed with some model recursion fixes from that version.

  12. venkata-denali commented on Jun 6, 2013

    @venkata-denali

    This is one of missing models. Please remove all the other models from pojo. And consider this only.

    @XmlRootElement(name="SubscriptionCriteria")
    public class SubscriptionCriteria {

    private String paramName;
    private String paramValue;
    private String paramDisplayValue;
    /**
     * @param paramName the paramName to set
     */
    public void setParamName(String paramName) {
        this.paramName = paramName;
    }
    /**
     * @return the paramName
     */
    public String getParamName() {
        return paramName;
    }
    /**
     * @param paramValue the paramValue to set
     */
    public void setParamValue(String paramValue) {
        this.paramValue = paramValue;
    }
    /**
     * @return the paramValue
     */
    public String getParamValue() {
        return paramValue;
    }
    /**
     * @param paramDisplayValue the paramDisplayValue to set
     */
    public void setParamDisplayValue(String paramDisplayValue) {
        this.paramDisplayValue = paramDisplayValue;
    }
    /**
     * @return the paramDisplayValue
     */
    public String getParamDisplayValue() {
        return paramDisplayValue;
    }
    

    }

    Thanks , 1.2.4-SNAPSHOT is look like not available. I am getting jar not available error in pom. I will wait for 1.2.4 and will see if this issue is fixed. Thank you very much.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions