如何从 Swagger API 声明生成 JSON-Schema

我有使用 斯威格 V1.2的服务的 Swagger API 声明。

我最初对 Swagger 的感觉是,它非常接近 JSON Schema (草案3和最近的草案4) ,并且相对容易为请求和响应对象生成 JSON Schema。

然而,虽然 Swagger 的一部分重用了 JSON Schema 结构,但结果是它只使用了一部分特性,而且它还在 Model 中引入了自己的继承(使用 subTypesdiscriminator)。

问题: 是否有任何现有的项目或代码片段,可以 从 Swagger API 声明生成可用的 JSON 模式

最佳 JSON 模式草案4和使用 Python (但我会很高兴找到任何东西)。

95139 次浏览

我刚写了一个工具 虚张声势似乎适合你的需要。

它加载 Swagger API 声明,并且能够将 Python 对象转换为/从 狂妄的原始人转换。还提供了一组客户端实现(包括 请求客户端) ,可以直接请求支持 Swagger 的服务。

这个工具可以解决我在改编 Python 中的 Swagger 时遇到的第一个问题,而且现在还很新。欢迎任何建议。

在与使用 Swagger 指定 REST API 并在相关测试套件中重用它进行了较长时间的斗争之后,我将与它分享我自己的经验(回答我自己的问题)。

Swagger 只支持 JSON 模式草案4的子集

对于 Swagger 1.2和2.0状态的规范,它只支持 JSON 模式草案4(s. 给你)的子集。这意味着:

  • 不能完全相信,每个有效的 JSON 模式都能得到 Swagger 的完全支持。
  • 考虑到 XML,Swagger 只支持 JSON 模式草案4提供的 JSON 结构子集的规范表示。

换句话说:

  • Swagger (1.2和2.0)不支持使用许多 JSON 结构,这些结构在 JSON 模式草案4中是有效的(同样适用于草案3)。
  • Swagger 不支持一般的 XML 数据结构,只允许非常受限的结构。

在实践中,您不能从用 JSON 或 XML 设计数据开始,而必须以 Swagger 开始和结束。

获取 JSON 模式在理论上是可行的,但并不容易

我花了一些时间编写了一个库,它将采用 Swagger API 规范并创建 JSON 模式草案4。我放弃了有几个原因:

  • 一点也不容易
  • 失望的发现是,我只能使用 JSON Schema 提供的子集。我们已经提出了一些 JSON 有效负载,必须开始修改它,以适应 Swagger 规范框架所允许的情况。

除了拥有非常漂亮的用于显示和测试 API 的 UI (是的,每个人都同意,它在视觉上非常令人愉快) ,我发现一个规范框架不允许我们使用我们想要的,但是给我们的设计增加了意想不到的限制,这很奇怪。

如果需要完整的 JSON 或 XML 模式支持,请使用 RAML

在研究其他 API 规范框架时,我发现了 RAML。由于它是通过支持任何 JSON 模式草案3/4或 W3C XML 模式1.0数据结构从头开始构建的,这种体验非常棒——我设计了有效负载的结构,我能够非常快速地编写 API 规范,并且根据定义的模式对真实请求和响应进行验证非常容易,因为模式是规范的基本组件,没有对它们加任何限制。

当时 RAML 的版本是0.8(版本1.0还没有发布)。

纠正问题才能得到真正的解决

好问题是解决问题的一半。我的问题是错误的,因为它没有满足我真正的期望。更正的问题是:

使用什么样的规范框架和技术,使用任意 JSON 模式草案4或 W3C XML 模式1.0定义的有效负载来指定 REST API。

我对这个问题的回答是:

  1. 在 JSON 模式草案4或 W3C XML 模式中设计有效负载
  2. 通过 RAML (目前为0.8版)描述您的 REST API。

可能还有其他可用的规范框架,但是 Swagger (v1.2和 v2.0都没有)肯定不是这种情况。除了提供很多特性(代码生成、非常漂亮的 API 文档等等)之外,它根本无法为上面提到的更新问题提供解决方案。

我曾经成功地这样做过:

Swagger.yaml-> proto-> jsonschema

我使用 Openapi2proto从 Swagger yaml 生成原型文件,然后使用 Proc-gen-jsonschema从中生成 JSONSchema。获得类型化的 JSONSchema 已经足够好了,但是 Proto3不支持“必需”类型,因此您错过了这一点。

有一个名为 Openapi2jsonschema的 Python 工具可以完成同样的工作。 您可以简单地使用 pip安装它。

Openapi2的自述文件展示了最简单的使用方法:

openapi2jsonschema https://raw.githubusercontent.com/kubernetes/kubernetes/master/api/openapi-spec/swagger.json

希望这个能帮上忙。

将 OpenApi 安装到 Jsonschema 提取器:

打开终端-执行以下命令

sudo yum install python-pip
pip install openapi2jsonschema
  • 将 openApi yaml 文件下载到一个文件夹中

  • Cd 到下载的文件夹,然后运行此命令

openapi2jsonschema --strict <openapi yaml filename>

我已经编写了一个递归函数,用于从 Swagger 定义创建 json 模式。

例如,考虑一下大摇大摆的规范 小型宠物店

定义 宠物被转换为下面的 json-schema

{
"description": null,
"type": "object",
"properties": {
"name": {
"description": null,
"type": "string"
},
"id": {
"format": "int64",
"description": null,
"type": "integer"
},
"tag": {
"description": null,
"type": "string"
}
}
}

当然,这是一个非常小的 json 模式,但是可以通过我编写的函数实现更多。让我解释一下我是如何做到这一点的,我使用以下 maven 依赖项从 swagger 规范获取定义

<dependency>
<groupId>io.swagger.parser.v3</groupId>
<artifactId>swagger-parser</artifactId>
<version>2.1.2</version>
</dependency>

为了创建 json 模式,我使用了下面的 maven 依赖项

<dependency>
<groupId>net.minidev</groupId>
<artifactId>json-smart</artifactId>
<version>2.4.7</version>
</dependency>

现在到编码部分,输入是 swagger 规范的位置以及需要转换为 json 模式的定义名称

public static void main(String[] args) {
String jsonSchema = SwaggerUtil.generateJsonSchemaFromSwaggerSpec("path to swagger spec", "Pet");
System.out.println(jsonSchema);
}

现在,我们需要处理作为输入传递的 swagger 定义,并递归地处理定义的属性

public static String generateJsonSchemaFromSwaggerSpec(String swaggerPath, String fieldName){


Swagger swagger = new SwaggerParser().read(swaggerPath);
Map<String, Model> definitions = swagger.getDefinitions();
Model schemaGenerationDefinition = definitions.get(fieldName);
Map<String, Property> propertyMap = schemaGenerationDefinition.getProperties();


Map<String, JsonProperty> customJsonPropertyMap = new HashMap<>();
propertyMap.forEach((propertyName, property) -> {
JsonProperty jsonProperty = processSwaggerProperties(propertyName, property, definitions);
customJsonPropertyMap.put(propertyName, jsonProperty);
});
JsonObjectProperty objectProperty = new JsonObjectProperty(customJsonPropertyMap, schemaGenerationDefinition.getDescription());
JSONObject generatedObject = objectProperty.toJsonObject();
String jsonSchema = generatedObject.toJSONString();
return jsonSchema;
}


private static JsonProperty processReference(String referenceName, String type, Map<String, Model> definitions){


Model model = definitions.get(referenceName);
Map<String, Property> propertyMap = model.getProperties();
Map<String, JsonProperty> jsonPropertyMap = new HashMap<>();
propertyMap.forEach((propertyName, property) -> {
JsonProperty jsonProperty = processSwaggerProperties(propertyName, property, definitions);
jsonPropertyMap.put(propertyName, jsonProperty);
});
if (type.equalsIgnoreCase("array")){
JsonArrayProperty jsonProperty = new JsonArrayProperty(model.getDescription());
jsonProperty.loadPropertiesFromMap(jsonPropertyMap);
return jsonProperty;
}else{
JsonObjectProperty objectProperty = new JsonObjectProperty(jsonPropertyMap, model.getDescription());
return objectProperty;
}
}


private static JsonProperty processSwaggerProperties(String propertyName, Property property, Map<String, Model> propertyDefinitions){
String definitionRefPath = "";
String type = "";
JsonProperty jsonProperty = null;
if (property.getType().equalsIgnoreCase("ref")){
definitionRefPath = ((RefProperty) property).getOriginalRef();
type = "object";
}else if (property.getType().equalsIgnoreCase("array")){
type = "array";
Property childProperty = ((ArrayProperty) property).getItems();
if (childProperty instanceof RefProperty){
RefProperty refProperty = (RefProperty) ((ArrayProperty) property).getItems();
definitionRefPath = refProperty.getOriginalRef();
}else{
JsonArrayProperty arrayProperty = new JsonArrayProperty(property.getDescription());
arrayProperty.loadChildProperty(childProperty);
return arrayProperty;
}
}else{
jsonProperty = PropertyFactory.createJsonProperty(property);
return jsonProperty;
}
String[] splitResult = definitionRefPath.split("/");
if (splitResult.length == 3) {
String propertyPath = splitResult[2];
System.out.println(propertyPath);
jsonProperty = processReference(propertyPath, type, propertyDefinitions);
}
return jsonProperty;
}

So for creating the json schema, I created my own custom json schema classes. that is for each of the json schema data types.. Also wrote from factory class to create the required json type

    public class PropertyFactory {




public static JsonProperty createJsonProperty(Property property){
JsonProperty jsonProperty = null;
switch (property.getType()){
case "number":
jsonProperty = new JsonNumberProperty(property.getFormat(), property.getDescription());
break;
case "string":
jsonProperty = new JsonStringProperty(property.getDescription());
break;
case "boolean":
jsonProperty = new JsonBooleanProperty(property.getDescription());
break;
case "integer":
jsonProperty = new JsonIntegerProperty(property.getFormat(), property.getDescription());
if (property instanceof IntegerProperty){
IntegerProperty integerProperty = (IntegerProperty) property;
if (integerProperty.getMinimum() != null)
((JsonIntegerProperty) jsonProperty).setMinimum(integerProperty.getMinimum());
if (integerProperty.getMaximum() != null)
((JsonIntegerProperty) jsonProperty).setMaximum(integerProperty.getMaximum());
}else if (property instanceof LongProperty){
LongProperty longProperty = (LongProperty) property;
if (longProperty.getMinimum() != null)
((JsonIntegerProperty) jsonProperty).setMinimum(longProperty.getMinimum());
if (longProperty.getMaximum() != null)
((JsonIntegerProperty) jsonProperty).setMaximum(longProperty.getMaximum());
}
break;
default:
System.out.println("Unhandled type");
}


return jsonProperty;
}
}


下面是我为每个 json 数据类型创建的抽象


public abstract class JsonProperty {


protected String type;


protected JSONArray required;


protected String description;




protected JsonProperty(String type, String description){
this.type = type;
this.description = description;
}


protected abstract JSONObject toJsonObject();


}



public class JsonArrayProperty extends JsonProperty{




private JsonProperty items;




public JsonArrayProperty(String description){
super("array", description);
}


@Override
protected JSONObject toJsonObject() {
JSONObject jsonObject = new JSONObject();
jsonObject.put("type", this.type);
jsonObject.put("description", this.description);
jsonObject.put("items", this.items.toJsonObject());
return jsonObject;
}


public void loadPropertiesFromMap(Map<String, JsonProperty> propertyMap){
this.items = new JsonObjectProperty(propertyMap, this.description);
}


public void loadChildProperty(Property childProperty){
this.items = PropertyFactory.createJsonProperty(childProperty);
}}


public class JsonObjectProperty extends JsonProperty{


private Map<String, JsonProperty> properties;




public JsonObjectProperty(Map<String, JsonProperty> properties, String description){
super("object", description);
this.properties = properties;
}


@Override
public JSONObject toJsonObject() {
JSONObject jsonObject = new JSONObject();
jsonObject.put("type", this.type);
jsonObject.put("description", this.description);
JSONObject propertyObject = new JSONObject();
this.properties.forEach((propertyName, jsonProperty) -> {
if (jsonProperty != null) {
JSONObject object = jsonProperty.toJsonObject();
propertyObject.put(propertyName, object);
}
});
jsonObject.put("properties", propertyObject);
return jsonObject;
}




public Map<String, JsonProperty> getProperties() {
return properties;
}


public void setProperties(Map<String, JsonProperty> properties) {
this.properties = properties;
}


}
public class JsonNumberProperty extends JsonProperty {


protected String format;


public JsonNumberProperty(String format, String description) {
super("number", description);
this.format = format;
}


public JsonNumberProperty(String type, String format, String description){
super(type, description);
this.format = format;
}


@Override
protected JSONObject toJsonObject() {
JSONObject jsonObject = new JSONObject();
jsonObject.put("type", this.type);
jsonObject.put("description", this.description);
if (this.format != null)
jsonObject.put("format", this.format);
return jsonObject;
}




}


public class JsonIntegerProperty extends JsonNumberProperty{


private String pattern;


private BigDecimal minimum;


private BigDecimal maximum;




public JsonIntegerProperty(String format, String description) {
super("integer", format, description);
}


@Override
protected JSONObject toJsonObject() {
JSONObject jsonObject = new JSONObject();
jsonObject.put("type", this.type);
jsonObject.put("description", this.description);
if (this.format != null)
jsonObject.put("format", this.format);
if (this.minimum != null)
jsonObject.put("minimum", this.minimum);
if (this.maximum != null)
jsonObject.put("maximum", this.maximum);
return jsonObject;
}




public void setPattern(String pattern) {
this.pattern = pattern;
}


public void setMinimum(BigDecimal minimum) {
this.minimum = minimum;
}


public void setMaximum(BigDecimal maximum) {
this.maximum = maximum;
}
}
public class JsonStringProperty extends JsonProperty{


public JsonStringProperty(String description) {
super("string", description);
}


@Override
protected JSONObject toJsonObject() {
JSONObject jsonObject = new JSONObject();
jsonObject.put("type", this.type);
jsonObject.put("description", this.description);
return jsonObject;
}
}

注意

这是一个自定义实现,我是为了满足自己的需要,如果遇到其他数据类型,可以通过扩展 JsonProperty类并提供 toJsonObject实现来简单地创建该类型。

编码愉快

使用 Swagger UI 作为文档,突出显示的链接返回 api 模式的 json 对象:

enter image description here