如何从Swagger ApiModelProperty注释生成示例POJO?

时间:2018-06-11 16:14:31

标签: java json swagger

我们正在创建一个REST API,使用Swagger的@ApiModelProperty注释进行记录。我正在为API编写端到端测试,我需要为某些请求生成JSON主体。假设我需要将以下JSON发布到端点:

{ "name": "dan", "age": "33" }

到目前为止,我创建了一个单独的类,其中包含所有必需的属性,并且可以使用Jackson序列化为JSON:

@JsonIgnoreProperties(ignoreUnknown = true)
public class MyPostRequest {
  private String name;
  private String age;
  // getters and fluid setters omitted...
  public static MyPostRequest getExample() {
    return new MyPostRequest().setName("dan").setAge("33");
  }
}

但是,我们注意到我们在代码库中已经有一个非常相似的类,它定义了API接受的模型。在此模型类中,每个属性的示例值已在@ApiModelProperty

中定义
@ApiModel(value = "MyAPIModel")
public class MyAPIModel extends AbstractModel {

  @ApiModelProperty(required = true, example = "dan")
  private String name;

  @ApiModelProperty(required = true, example = "33")
  private String age;

}

是否有一种简单的方法来生成一个MyAPIModel实例,其中填充了每个属性的示例值?注意:我需要能够在转换为JSON之前修改端到端测试中的单个属性,以便测试不同的边缘情况。因此,直接生成示例JSON是不够的。

基本上,我可以在MyAPIModel上编写一个静态方法getExample()(甚至在基类AbstractModel上更好),它返回Swagger注释中指定的MyAPIModel的示例实例吗?

1 个答案:

答案 0 :(得分:1)

截至本回答时,这似乎不可能。我找到的最接近的可能性是:

  1. io.swagger.converter.ModelConverters:方法read()创建Model个对象,但这些模型中的example成员为空。这些示例以String形式存在于properties成员中(直接取自APIModelParameter注释)。

  2. io.swagger.codegen.examples.ExampleGenerator:方法resolveModelToExample()ModelConverters.read()获取输出,并生成一个表示对象及其属性的Map(同时还解析非字符串属性,如嵌套楷模)。此方法用于序列化为JSON。不幸的是,resolveModelToExample()是私有的。如果它是公共可访问的,那么为带注释的Swagger API模型类生成模型默认值的代码可能如下所示:

  3. protected <T extends AbstractModel> T getModelExample(Class<T> clazz) {
        // Get the swagger model instance including properties list with examples
        Map<String,Model> models = ModelConverters.getInstance().read(clazz);
        // Parse non-string example values into proper objects, and compile a map of properties representing an example object
        ExampleGenerator eg = new ExampleGenerator(models);
        Object resolved = eg.resolveModelToExample(clazz.getSimpleName(), null, new HashSet<String>());
        if (!(resolved instanceof Map<?,?>)) {
            // Model is not an instance of io.swagger.models.ModelImpl, and therefore no example can be resolved
            return null;
        }
        T result = clazz.newInstance();
        BeanUtils.populate(result, (Map<?,?>) resolved);
        return result;
    }
    
    1. 因为在我们的例子中我们需要的是String,boolean和int属性,所以至少有可能以疯狂的hackish方式解析注释:
    2. protected <T extends MyModelBaseClass> T getModelExample(Class<T> clazz) {
          try {
              T result = clazz.newInstance();
              for(Field field  : clazz.getDeclaredFields()) {
                  if (field.isAnnotationPresent(ApiModelProperty.class)) {
                      String exampleValue = field.getAnnotation(ApiModelProperty.class).example();
                      if (exampleValue != null) {
                          boolean accessible = field.isAccessible();
                          field.setAccessible(true);
                          setField(result, field, exampleValue);
                          field.setAccessible(accessible);
                      }
                  }
              }
              return result;
          } catch (InstantiationException | IllegalAccessException e) {
              throw new IllegalArgumentException("Could not create model example", e);
          }
      }
      
      private <T extends MyModelBaseClass> void setField(T model, Field field, String value) throws IllegalArgumentException, IllegalAccessException {
          Class<?> type = field.getType();
          LOGGER.info(type.toString());
          if (String.class.equals(type)) {
              field.set(model, value);
          } else if (Boolean.TYPE.equals(type) || Boolean.class.equals(type)) {
              field.set(model, Boolean.parseBoolean(value));
          } else if (Integer.TYPE.equals(type) || Integer.class.equals(type)) {
              field.set(model, Integer.parseInt(value));
          }
      }
      

      我可能会在Github上打开一个Issue / PR,建议为Swagger添加功能。我很惊讶没有其他人似乎要求这个功能,因为我们将示例模型实例作为测试发送到API的用例应该是常见的。