I've written one recursive function for creating json schema from Swagger definition.
For example, consider the swagger specification petstore-minimal
The definition Pet is converted into the below json-schema
{
"description": null,
"type": "object",
"properties": {
"name": {
"description": null,
"type": "string"
},
"id": {
"format": "int64",
"description": null,
"type": "integer"
},
"tag": {
"description": null,
"type": "string"
}
}
}
Ofcourse, this is a very minimal json schema, but can be achieved much more with the function that I wrote. Let me explain the way I did this, I used the following maven dependency to fetch the definitions from swagger specification
<dependency>
<groupId>io.swagger.parser.v3</groupId>
<artifactId>swagger-parser</artifactId>
<version>2.1.2</version>
</dependency>
To create the json schema, I used the below maven dependency
<dependency>
<groupId>net.minidev</groupId>
<artifactId>json-smart</artifactId>
<version>2.4.7</version>
</dependency>
Now to the coding part, the input is the location of the swagger spec and also the definition name which we need to convert to json schema
public static void main(String[] args) {
String jsonSchema = SwaggerUtil.generateJsonSchemaFromSwaggerSpec("path to swagger spec", "Pet");
System.out.println(jsonSchema);
}
Now, we need to process the swagger definition passed as the input and recursively process the properties of the definition
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;
}
}
Below are the abstractions I created for each of the json datatypes
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;
}
}
NOTE
This is a custom implementation that I did for my needs, if you come across any additional datatypes, you can simply create the type by extending the JsonProperty
class and providing the toJsonObject
implementation.
Happy Coding