Repository: swagger-api/swagger-parser
Pull request #1494 (2020-11-26 15:57:10)

feat: Add Inline Resolver to OpenAPI 2.0 parser
This change allows allow swagger-parser consumers who are using OpenAPI 2.0 APIs to flatten definition models in the returned spec similar to OpenAPI 3.0 API can be flatten when using swagger-parser 2.x

* Implementation of InlineModelResolver.java and InlineModelResolverTest.java are taken directly from swagger-codegen project. Only modifications are to the package name. This was intentional so all behaviour is consistent.
* Added two additional interfaces in SwaggerParser.java for reading the input spec; these take a `ParseOptions` object - this is based on the interface used in swagger-parser 2.x


Patch:
diff --git a/modules/swagger-parser/src/main/java/io/swagger/parser/SwaggerParser.java b/modules/swagger-parser/src/main/java/io/swagger/parser/SwaggerParser.java
index 9e97b85e..dcea5b89 100644
--- a/modules/swagger-parser/src/main/java/io/swagger/parser/SwaggerParser.java
+++ b/modules/swagger-parser/src/main/java/io/swagger/parser/SwaggerParser.java
@@ -5,6 +5,8 @@ import com.fasterxml.jackson.databind.ObjectMapper;
 import io.swagger.models.Swagger;
 import io.swagger.models.auth.AuthorizationValue;
 import io.swagger.parser.util.DeserializationUtils;
+import io.swagger.parser.util.InlineModelResolver;
+import io.swagger.parser.util.ParseOptions;
 import io.swagger.parser.util.SwaggerDeserializationResult;
 import io.swagger.util.Json;
 
@@ -59,6 +61,12 @@ public class SwaggerParser {
     }
 
     public Swagger read(String location, List<AuthorizationValue> auths, boolean resolve) {
+        ParseOptions options = new ParseOptions();
+        options.setResolve(resolve);
+        return read(location, auths, options);
+    }
+
+    public Swagger read(String location, List<AuthorizationValue> auths, ParseOptions options) {
         if (location == null) {
             return null;
         }
@@ -68,11 +76,16 @@ public class SwaggerParser {
         try {
             output = new Swagger20Parser().read(location, auths);
             if (output != null) {
-            	if(resolve){
-            		return new SwaggerResolver(output, auths, location).resolve();
-            	} else {
-            		return output;
-            	}
+                if (options != null) {
+                    if (options.isResolve()) {
+                        output = new SwaggerResolver(output, auths, location).resolve();
+                    }
+                    if (options.isFlatten()) {
+                        InlineModelResolver inlineModelResolver = new InlineModelResolver();
+                        inlineModelResolver.flatten(output);
+                    }
+                }
+                return output;
             }
         } catch (IOException e) {
         }
@@ -146,14 +159,20 @@ public class SwaggerParser {
     }
 
     public Swagger read(JsonNode node) {
-        return read(node, new ArrayList<AuthorizationValue>(), false);
+        return read(node, false);
     }
 
     public Swagger read(JsonNode node, boolean resolve) {
-        return read(node, new ArrayList<AuthorizationValue>(), resolve);
+        return read(node, new ArrayList<>(), resolve);
     }
 
     public Swagger read(JsonNode node, List<AuthorizationValue> authorizationValues, boolean resolve) {
+        ParseOptions options = new ParseOptions();
+        options.setResolve(resolve);
+        return read(node, authorizationValues, options);
+    }
+
+    public Swagger read(JsonNode node, List<AuthorizationValue> authorizationValues, ParseOptions options) {
         if (node == null) {
             return null;
         }
@@ -164,11 +183,16 @@ public class SwaggerParser {
         try {
             output = new Swagger20Parser().read(node);
             if (output != null) {
-                if (resolve) {
-                    return new SwaggerResolver(output, authorizationValues).resolve();
-                } else {
-                    return output;
+                if (options != null) {
+                    if (options.isResolve()) {
+                        output = new SwaggerResolver(output, authorizationValues).resolve();
+                    }
+                    if (options.isFlatten()) {
+                        InlineModelResolver inlineModelResolver = new InlineModelResolver();
+                        inlineModelResolver.flatten(output);
+                    }
                 }
+                return output;
             }
         } catch (IOException e) {
         }
diff --git a/modules/swagger-parser/src/main/java/io/swagger/parser/util/InlineModelResolver.java b/modules/swagger-parser/src/main/java/io/swagger/parser/util/InlineModelResolver.java
new file mode 100644
index 00000000..1574e3b0
--- /dev/null
+++ b/modules/swagger-parser/src/main/java/io/swagger/parser/util/InlineModelResolver.java
@@ -0,0 +1,528 @@
+package io.swagger.parser.util;
+
+import io.swagger.models.*;
+import io.swagger.models.parameters.BodyParameter;
+import io.swagger.models.parameters.Parameter;
+import io.swagger.models.properties.*;
+import io.swagger.models.utils.PropertyModelConverter;
+import io.swagger.util.Json;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+public class InlineModelResolver {
+    private Swagger swagger;
+    private boolean skipMatches;
+    static Logger LOGGER = LoggerFactory.getLogger(InlineModelResolver.class);
+
+    Map<String, Model> addedModels = new HashMap<String, Model>();
+    Map<String, String> generatedSignature = new HashMap<String, String>();
+
+    public void flatten(Swagger swagger) {
+        this.swagger = swagger;
+
+        if (swagger.getDefinitions() == null) {
+            swagger.setDefinitions(new HashMap<String, Model>());
+        }
+
+        // operations
+        Map<String, Path> paths = swagger.getPaths();
+        Map<String, Model> models = swagger.getDefinitions();
+
+        if (paths != null) {
+            for (String pathname : paths.keySet()) {
+                Path path = paths.get(pathname);
+
+                for (Operation operation : path.getOperations()) {
+                    List<Parameter> parameters = operation.getParameters();
+
+                    if (parameters != null) {
+                        for (Parameter parameter : parameters) {
+                            if (parameter instanceof BodyParameter) {
+                                BodyParameter bp = (BodyParameter) parameter;
+                                if (bp.getSchema() != null) {
+                                    Model model = bp.getSchema();
+                                    if (model instanceof ModelImpl) {
+                                        ModelImpl obj = (ModelImpl) model;
+                                        if (obj.getType() == null || "object".equals(obj.getType())) {
+                                            if (obj.getProperties() != null && obj.getProperties().size() > 0) {
+                                                flattenProperties(obj.getProperties(), pathname);
+                                                String modelName = resolveModelName(obj.getTitle(), bp.getName());
+                                                bp.setSchema(new RefModel(modelName));
+                                                addGenerated(modelName, model);
+                                                swagger.addDefinition(modelName, model);
+                                            }
+                                        }
+                                    } else if (model instanceof ArrayModel) {
+                                        ArrayModel am = (ArrayModel) model;
+                                        Property inner = am.getItems();
+
+                                        if (inner instanceof ObjectProperty) {
+                                            ObjectProperty op = (ObjectProperty) inner;
+                                            if (op.getProperties() != null && op.getProperties().size() > 0) {
+                                                flattenProperties(op.getProperties(), pathname);
+                                                String modelName = resolveModelName(op.getTitle(), bp.getName());
+                                                Model innerModel = modelFromProperty(op, modelName);
+                                                String existing = matchGenerated(innerModel);
+                                                if (existing != null) {
+                                                    RefProperty refProperty = new RefProperty(existing);
+                                                    refProperty.setRequired(op.getRequired());
+                                                    am.setItems(refProperty);
+                                                } else {
+                                                    RefProperty refProperty = new RefProperty(modelName);
+                                                    refProperty.setRequired(op.getRequired());
+                                                    am.setItems(refProperty);
+                                                    addGenerated(modelName, innerModel);
+                                                    swagger.addDefinition(modelName, innerModel);
+                                                }
+                                            }
+                                        }
+                                    }
+                                }
+                            }
+                        }
+                    }
+                    Map<String, Response> responses = operation.getResponses();
+                    if (responses != null) {
+                        for (String key : responses.keySet()) {
+                            Response response = responses.get(key);
+                            if (response.getSchema() != null) {
+                                Property property = response.getSchema();
+                                if (property instanceof ObjectProperty) {
+                                    ObjectProperty op = (ObjectProperty) property;
+                                    if (op.getProperties() != null && op.getProperties().size() > 0) {
+                                        String modelName = resolveModelName(op.getTitle(), "inline_response_" + key);
+                                        Model model = modelFromProperty(op, modelName);
+                                        String existing = matchGenerated(model);
+                                        if (existing != null) {
+                                            Property refProperty = this.makeRefProperty(existing, property);
+                                            refProperty.setRequired(op.getRequired());
+                                            response.setResponseSchema(new PropertyModelConverter().propertyToModel(refProperty));
+                                        } else {
+                                            Property refProperty = this.makeRefProperty(modelName, property);
+                                            refProperty.setRequired(op.getRequired());
+                                            response.setResponseSchema(new PropertyModelConverter().propertyToModel(refProperty));
+                                            addGenerated(modelName, model);
+                                            swagger.addDefinition(modelName, model);
+                                        }
+                                    }
+                                } else if (property instanceof ArrayProperty) {
+                                    ArrayProperty ap = (ArrayProperty) property;
+                                    Property inner = ap.getItems();
+
+                                    if (inner instanceof ObjectProperty) {
+                                        ObjectProperty op = (ObjectProperty) inner;
+                                        if (op.getProperties() != null && op.getProperties().size() > 0) {
+                                            flattenProperties(op.getProperties(), pathname);
+                                            String modelName = resolveModelName(op.getTitle(),
+                                                    "inline_response_" + key);
+                                            Model innerModel = modelFromProperty(op, modelName);
+                                            String existing = matchGenerated(innerModel);
+                                            if (existing != null) {
+                                                Property refProperty = this.makeRefProperty(existing, op);
+                                                refProperty.setRequired(op.getRequired());
+                                                ap.setItems(refProperty);
+                                                response.setResponseSchema(new PropertyModelConverter().propertyToModel(ap));
+                                            } else {
+                                                Property refProperty = this.makeRefProperty(modelName, op);
+                                                refProperty.setRequired(op.getRequired());
+                                                ap.setItems(refProperty);
+                                                response.setResponseSchema(new PropertyModelConverter().propertyToModel(ap));
+                                                addGenerated(modelName, innerModel);
+                                                swagger.addDefinition(modelName, innerModel);
+                                            }
+                                        }
+                                    }
+                                } else if (property instanceof MapProperty) {
+                                    MapProperty mp = (MapProperty) property;
+
+                                    Property innerProperty = mp.getAdditionalProperties();
+                                    if (innerProperty instanceof ObjectProperty) {
+                                        ObjectProperty op = (ObjectProperty) innerProperty;
+                                        if (op.getProperties() != null && op.getProperties().size() > 0) {
+                                            flattenProperties(op.getProperties(), pathname);
+                                            String modelName = resolveModelName(op.getTitle(),
+                                                    "inline_response_" + key);
+                                            Model innerModel = modelFromProperty(op, modelName);
+                                            String existing = matchGenerated(innerModel);
+                                            if (existing != null) {
+                                                RefProperty refProperty = new RefProperty(existing);
+                                                refProperty.setRequired(op.getRequired());
+                                                mp.setAdditionalProperties(refProperty);
+                                                response.setResponseSchema(new PropertyModelConverter().propertyToModel(mp));
+                                            } else {
+                                                RefProperty refProperty = new RefProperty(modelName);
+                                                refProperty.setRequired(op.getRequired());
+                                                mp.setAdditionalProperties(refProperty);
+                                                response.setResponseSchema(new PropertyModelConverter().propertyToModel(mp));
+                                                addGenerated(modelName, innerModel);
+                                                swagger.addDefinition(modelName, innerModel);
+                                            }
+                                        }
+                                    }
+                                }
+                            }
+                        }
+                    }
+                }
+            }
+        }
+
+        // definitions
+        if (models != null) {
+            List<String> modelNames = new ArrayList<String>(models.keySet());
+            for (String modelName : modelNames) {
+                Model model = models.get(modelName);
+                if (model instanceof ModelImpl) {
+                    ModelImpl m = (ModelImpl) model;
+
+                    Map<String, Property> properties = m.getProperties();
+                    flattenProperties(properties, modelName);
+                    fixStringModel(m);
+                } else if (model instanceof ArrayModel) {
+                    ArrayModel m = (ArrayModel) model;
+                    Property inner = m.getItems();
+                    if (inner instanceof ObjectProperty) {
+                        ObjectProperty op = (ObjectProperty) inner;
+                        if (op.getProperties() != null && op.getProperties().size() > 0) {
+                            String innerModelName = resolveModelName(op.getTitle(), modelName + "_inner");
+                            Model innerModel = modelFromProperty(op, innerModelName);
+                            String existing = matchGenerated(innerModel);
+                            if (existing == null) {
+                                swagger.addDefinition(innerModelName, innerModel);
+                                addGenerated(innerModelName, innerModel);
+                                RefProperty refProperty = new RefProperty(innerModelName);
+                                refProperty.setRequired(op.getRequired());
+                                m.setItems(refProperty);
+                            } else {
+                                RefProperty refProperty = new RefProperty(existing);
+                                refProperty.setRequired(op.getRequired());
+                                m.setItems(refProperty);
+                            }
+                        }
+                    }
+                } else if (model instanceof ComposedModel) {
+                    ComposedModel m = (ComposedModel) model;
+                    if (m.getChild() != null) {
+                        Map<String, Property> properties = m.getChild().getProperties();
+                        flattenProperties(properties, modelName);
+                    }
+                }
+            }
+        }
+    }
+
+    /**
+     * This function fix models that are string (mostly enum). Before this fix, the example
+     * would look something like that in the doc: "\"example from def\""
+     * @param m Model implementation
+     */
+    private void fixStringModel(ModelImpl m) {
+        if (m.getType() != null && m.getType().equals("string") && m.getExample() != null) {
+            String example = m.getExample().toString();
+            if (!example.isEmpty() && example.substring(0, 1).equals("\"") &&
+                    example.substring(example.length() - 1).equals("\"")) {
+                m.setExample(example.substring(1, example.length() - 1));
+            }
+        }
+    }
+
+    private String resolveModelName(String title, String key) {
+        if (title == null) {
+            return uniqueName(key);
+        } else {
+            return uniqueName(title);
+        }
+    }
+
+    public String matchGenerated(Model model) {
+        if (this.skipMatches) {
+            return null;
+        }
+        String json = Json.pretty(model);
+        if (generatedSignature.containsKey(json)) {
+            return generatedSignature.get(json);
+        }
+        return null;
+    }
+
+    public void addGenerated(String name, Model model) {
+        generatedSignature.put(Json.pretty(model), name);
+    }
+
+    public String uniqueName(String key) {
+        int count = 0;
+        boolean done = false;
+        key = key.replaceAll("[^a-z_\\.A-Z0-9 ]", ""); // FIXME: a parameter
+        // should not be
+        // assigned. Also declare
+        // the methods parameters
+        // as 'final'.
+        while (!done) {
+            String name = key;
+            if (count > 0) {
+                name = key + "_" + count;
+            }
+            if (swagger.getDefinitions() == null) {
+                return name;
+            } else if (!swagger.getDefinitions().containsKey(name)) {
+                return name;
+            }
+            count += 1;
+        }
+        return key;
+    }
+
+    public void flattenProperties(Map<String, Property> properties, String path) {
+        if (properties == null) {
+            return;
+        }
+        Map<String, Property> propsToUpdate = new HashMap<String, Property>();
+        Map<String, Model> modelsToAdd = new HashMap<String, Model>();
+        for (String key : properties.keySet()) {
+            Property property = properties.get(key);
+            if (property instanceof ObjectProperty && ((ObjectProperty) property).getProperties() != null
+                    && ((ObjectProperty) property).getProperties().size() > 0) {
+
+                ObjectProperty op = (ObjectProperty) property;
+
+                String modelName = resolveModelName(op.getTitle(), path + "_" + key);
+                Model model = modelFromProperty(op, modelName);
+
+                String existing = matchGenerated(model);
+
+                if (existing != null) {
+                    RefProperty refProperty = new RefProperty(existing);
+                    refProperty.setRequired(op.getRequired());
+                    propsToUpdate.put(key, refProperty);
+                } else {
+                    RefProperty refProperty = new RefProperty(modelName);
+                    refProperty.setRequired(op.getRequired());
+                    propsToUpdate.put(key, refProperty);
+                    modelsToAdd.put(modelName, model);
+                    addGenerated(modelName, model);
+                    swagger.addDefinition(modelName, model);
+                }
+            } else if (property instanceof ArrayProperty) {
+                ArrayProperty ap = (ArrayProperty) property;
+                Property inner = ap.getItems();
+
+                if (inner instanceof ObjectProperty) {
+                    ObjectProperty op = (ObjectProperty) inner;
+                    if (op.getProperties() != null && op.getProperties().size() > 0) {
+                        flattenProperties(op.getProperties(), path);
+                        String modelName = resolveModelName(op.getTitle(), path + "_" + key);
+                        Model innerModel = modelFromProperty(op, modelName);
+                        String existing = matchGenerated(innerModel);
+                        if (existing != null) {
+                            RefProperty refProperty = new RefProperty(existing);
+                            refProperty.setRequired(op.getRequired());
+                            ap.setItems(refProperty);
+                        } else {
+                            RefProperty refProperty = new RefProperty(modelName);
+                            refProperty.setRequired(op.getRequired());
+                            ap.setItems(refProperty);
+                            addGenerated(modelName, innerModel);
+                            swagger.addDefinition(modelName, innerModel);
+                        }
+                    }
+                }
+            } else if (property instanceof MapProperty) {
+                MapProperty mp = (MapProperty) property;
+                Property inner = mp.getAdditionalProperties();
+
+                if (inner instanceof ObjectProperty) {
+                    ObjectProperty op = (ObjectProperty) inner;
+                    if (op.getProperties() != null && op.getProperties().size() > 0) {
+                        flattenProperties(op.getProperties(), path);
+                        String modelName = resolveModelName(op.getTitle(), path + "_" + key);
+                        Model innerModel = modelFromProperty(op, modelName);
+                        String existing = matchGenerated(innerModel);
+                        if (existing != null) {
+                            RefProperty refProperty = new RefProperty(existing);
+                            refProperty.setRequired(op.getRequired());
+                            mp.setAdditionalProperties(refProperty);
+                        } else {
+                            RefProperty refProperty = new RefProperty(modelName);
+                            refProperty.setRequired(op.getRequired());
+                            mp.setAdditionalProperties(refProperty);
+                            addGenerated(modelName, innerModel);
+                            swagger.addDefinition(modelName, innerModel);
+                        }
+                    }
+                }
+            } else if (property instanceof ComposedProperty) {
+                ComposedProperty composedProperty = (ComposedProperty) property;
+                String modelName = resolveModelName(composedProperty.getTitle(), path + "_" + key);
+                Model model = modelFromProperty(composedProperty, modelName);
+                String existing = matchGenerated(model);
+                if (existing != null) {
+                    RefProperty refProperty = new RefProperty(existing);
+                    refProperty.setRequired(composedProperty.getRequired());
+                    propsToUpdate.put(key, refProperty);
+                } else {
+                    RefProperty refProperty = new RefProperty(modelName);
+                    refProperty.setRequired(composedProperty.getRequired());
+                    propsToUpdate.put(key, refProperty);
+                    addGenerated(modelName, model);
+                    swagger.addDefinition(modelName, model);
+                }
+            }
+        }
+        if (propsToUpdate.size() > 0) {
+            for (String key : propsToUpdate.keySet()) {
+                properties.put(key, propsToUpdate.get(key));
+            }
+        }
+        for (String key : modelsToAdd.keySet()) {
+            swagger.addDefinition(key, modelsToAdd.get(key));
+            this.addedModels.put(key, modelsToAdd.get(key));
+        }
+    }
+
+    @SuppressWarnings("static-method")
+    public Model modelFromProperty(ArrayProperty object, @SuppressWarnings("unused") String path) {
+        String description = object.getDescription();
+        String example = null;
+
+        Object obj = object.getExample();
+        if (obj != null) {
+            example = obj.toString();
+        }
+
+        Property inner = object.getItems();
+        if (inner instanceof ObjectProperty) {
+            ArrayModel model = new ArrayModel();
+            model.setDescription(description);
+            model.setExample(example);
+            model.setItems(object.getItems());
+            if (object.getVendorExtensions() != null) {
+                for (String key : object.getVendorExtensions().keySet()) {
+                    model.setVendorExtension(key, object.getVendorExtensions().get(key));
+                }
+            }
+
+            return model;
+        }
+
+        return null;
+    }
+
+    public Model modelFromProperty(ObjectProperty object, String path) {
+        String description = object.getDescription();
+        String example = null;
+
+        Object obj = object.getExample();
+        if (obj != null) {
+            example = obj.toString();
+        }
+        String name = object.getName();
+        Xml xml = object.getXml();
+        Map<String, Property> properties = object.getProperties();
+
+        ModelImpl model = new ModelImpl();
+        model.type(object.getType());
+        model.setDescription(description);
+        model.setExample(example);
+        model.setName(name);
+        model.setXml(xml);
+        if (object.getVendorExtensions() != null) {
+            for (String key : object.getVendorExtensions().keySet()) {
+                model.setVendorExtension(key, object.getVendorExtensions().get(key));
+            }
+        }
+
+        if (properties != null) {
+            flattenProperties(properties, path);
+            model.setProperties(properties);
+        }
+
+        return model;
+    }
+
+    public Model modelFromProperty(ComposedProperty composedProperty, String path) {
+        String description = composedProperty.getDescription();
+        String example = null;
+
+        Object obj = composedProperty.getExample();
+        if (obj != null) {
+            example = obj.toString();
+        }
+        Xml xml = composedProperty.getXml();
+
+        ModelImpl model = new ModelImpl();
+        model.type(composedProperty.getType());
+        model.setDescription(description);
+        model.setExample(example);
+        model.setName(path);
+        model.setXml(xml);
+        if (composedProperty.getVendorExtensions() != null) {
+            for (String key : composedProperty.getVendorExtensions().keySet()) {
+                model.setVendorExtension(key, composedProperty.getVendorExtensions().get(key));
+            }
+        }
+        return model;
+    }
+
+    @SuppressWarnings("static-method")
+    public Model modelFromProperty(MapProperty object, @SuppressWarnings("unused") String path) {
+        String description = object.getDescription();
+        String example = null;
+
+        Object obj = object.getExample();
+        if (obj != null) {
+            example = obj.toString();
+        }
+
+        ArrayModel model = new ArrayModel();
+        model.setDescription(description);
+        model.setExample(example);
+        model.setItems(object.getAdditionalProperties());
+        if (object.getVendorExtensions() != null) {
+            for (String key : object.getVendorExtensions().keySet()) {
+                model.setVendorExtension(key, object.getVendorExtensions().get(key));
+            }
+        }
+
+        return model;
+    }
+
+    /**
+     * Make a RefProperty
+     *
+     * @param ref new property name
+     * @param property Property
+     * @return {@link Property} A constructed Swagger property
+     */
+    public Property makeRefProperty(String ref, Property property) {
+        RefProperty newProperty = new RefProperty(ref);
+        this.copyVendorExtensions(property, newProperty);
+        return newProperty;
+    }
+
+    /**
+     * Copy vendor extensions from Property to another Property
+     *
+     * @param source source property
+     * @param target target property
+     */
+    public void copyVendorExtensions(Property source, AbstractProperty target) {
+        Map<String, Object> vendorExtensions = source.getVendorExtensions();
+        for (String extName : vendorExtensions.keySet()) {
+            target.setVendorExtension(extName, vendorExtensions.get(extName));
+        }
+    }
+
+    public boolean isSkipMatches() {
+        return skipMatches;
+    }
+
+    public void setSkipMatches(boolean skipMatches) {
+        this.skipMatches = skipMatches;
+    }
+
+}
diff --git a/modules/swagger-parser/src/main/java/io/swagger/parser/util/ParseOptions.java b/modules/swagger-parser/src/main/java/io/swagger/parser/util/ParseOptions.java
new file mode 100644
index 00000000..64129f58
--- /dev/null
+++ b/modules/swagger-parser/src/main/java/io/swagger/parser/util/ParseOptions.java
@@ -0,0 +1,18 @@
+package io.swagger.parser.util;
+
+public class ParseOptions {
+    private boolean resolve;
+    private boolean flatten;
+
+    public boolean isResolve() {
+        return resolve;
+    }
+
+    public void setResolve(boolean resolve) {
+        this.resolve = resolve;
+    }
+
+    public boolean isFlatten() { return flatten; }
+
+    public void setFlatten(boolean flatten) { this.flatten = flatten; }
+}


Test patch:
diff --git a/modules/swagger-parser/src/test/java/io/swagger/parser/SwaggerParserTest.java b/modules/swagger-parser/src/test/java/io/swagger/parser/SwaggerParserTest.java
index 0398e423..dffcd613 100644
--- a/modules/swagger-parser/src/test/java/io/swagger/parser/SwaggerParserTest.java
+++ b/modules/swagger-parser/src/test/java/io/swagger/parser/SwaggerParserTest.java
@@ -8,14 +8,13 @@ import static org.testng.Assert.assertTrue;
 import static org.testng.Assert.fail;
 
 import java.io.File;
+import java.io.IOException;
 import java.math.BigDecimal;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
+import java.util.*;
 
+import io.swagger.parser.util.DeserializationUtils;
+import io.swagger.parser.util.ParseOptions;
+import org.apache.commons.io.FileUtils;
 import org.testng.Assert;
 import org.testng.annotations.Test;
 import org.testng.reporters.Files;
@@ -1655,8 +1654,6 @@ public class SwaggerParserTest {
         SwaggerDeserializationResult result = new SwaggerParser().readWithInfo("src/test/resources/issue-1432.yaml", null, true);
         assertNotNull(result);
         assertEquals("attribute paths.'/tickets'(get).responses.200.title is unexpected",result.getMessages().get(0));
-
-
     }
 
     @Test
@@ -1672,4 +1669,40 @@ public class SwaggerParserTest {
 
         assertEquals("password", composedModel.getRequired().get(0));
     }
+
+    @Test
+    public void testInlineModelResolver() throws IOException {
+        String inputSpec = FileUtils.readFileToString(new File("src/test/resources/flatten.json"));
+        JsonNode rootNode = DeserializationUtils.deserializeIntoTree(inputSpec, "");
+        ParseOptions parseOptions = new ParseOptions();
+        parseOptions.setFlatten(true);
+
+        Swagger swagger = new SwaggerParser().read(rootNode, new ArrayList<>(), parseOptions);
+
+        assertNotNull(swagger);
+        Map<String, Model> definitions = swagger.getDefinitions();
+        assertTrue(definitions.containsKey("User"));
+        assertTrue(definitions.containsKey("User_address"));
+
+        Model userAddress = definitions.get("User_address");
+        assertTrue(userAddress.getProperties().containsKey("city"));
+        assertTrue(userAddress.getProperties().containsKey("street"));
+    }
+
+    @Test
+    public void testInlineModelResolverByLocation() {
+        ParseOptions parseOptions = new ParseOptions();
+        parseOptions.setFlatten(true);
+
+        Swagger swagger = new SwaggerParser().read("src/test/resources/flatten.json", new ArrayList<>(), parseOptions);
+
+        assertNotNull(swagger);
+        Map<String, Model> definitions = swagger.getDefinitions();
+        assertTrue(definitions.containsKey("User"));
+        assertTrue(definitions.containsKey("User_address"));
+
+        Model userAddress = definitions.get("User_address");
+        assertTrue(userAddress.getProperties().containsKey("city"));
+        assertTrue(userAddress.getProperties().containsKey("street"));
+    }
 }
diff --git a/modules/swagger-parser/src/test/java/io/swagger/parser/util/InlineModelResolverTest.java b/modules/swagger-parser/src/test/java/io/swagger/parser/util/InlineModelResolverTest.java
new file mode 100644
index 00000000..d02bd1cc
--- /dev/null
+++ b/modules/swagger-parser/src/test/java/io/swagger/parser/util/InlineModelResolverTest.java
@@ -0,0 +1,1021 @@
+package io.swagger.parser.util;
+
+import io.swagger.models.*;
+import io.swagger.models.parameters.BodyParameter;
+import io.swagger.models.parameters.Parameter;
+import io.swagger.models.properties.*;
+import io.swagger.util.Json;
+import org.apache.commons.lang3.StringUtils;
+import org.testng.annotations.Test;
+
+import java.util.Map;
+
+import static org.testng.AssertJUnit.*;
+
+@SuppressWarnings("static-method")
+public class InlineModelResolverTest {
+    @Test
+    public void resolveInlineModelTestWithoutTitle() throws Exception {
+        Swagger swagger = new Swagger();
+
+        swagger.addDefinition("User", new ModelImpl()
+                .name("user")
+                .description("a common user")
+                .property("name", new StringProperty())
+                .property("address", new ObjectProperty()
+                        ._default("default")
+                        .access("access")
+                        .readOnly(false)
+                        .required(true)
+                        .description("description")
+                        .name("name")
+                        .property("street", new StringProperty())
+                        .property("city", new StringProperty())));
+
+        new io.swagger.parser.util.InlineModelResolver().flatten(swagger);
+
+        ModelImpl user = (ModelImpl)swagger.getDefinitions().get("User");
+
+        assertNotNull(user);
+        assertTrue(user.getProperties().get("address") instanceof RefProperty);
+
+        ModelImpl address = (ModelImpl)swagger.getDefinitions().get("User_address");
+        assertNotNull(address);
+        assertNotNull(address.getProperties().get("city"));
+        assertNotNull(address.getProperties().get("street"));
+    }
+
+    @Test
+    public void resolveInlineModelTestWithTitle() throws Exception {
+        Swagger swagger = new Swagger();
+
+        swagger.addDefinition("User", new ModelImpl()
+                .name("user")
+                .description("a common user")
+                .property("name", new StringProperty())
+                .property("address", new ObjectProperty()
+                        .title("UserAddressTitle")
+                        ._default("default")
+                        .access("access")
+                        .readOnly(false)
+                        .required(true)
+                        .description("description")
+                        .name("name")
+                        .property("street", new StringProperty())
+                        .property("city", new StringProperty())));
+
+        new io.swagger.parser.util.InlineModelResolver().flatten(swagger);
+
+        ModelImpl user = (ModelImpl)swagger.getDefinitions().get("User");
+
+        assertNotNull(user);
+        assertTrue(user.getProperties().get("address") instanceof RefProperty);
+
+        ModelImpl address = (ModelImpl)swagger.getDefinitions().get("UserAddressTitle");
+        assertNotNull(address);
+        assertNotNull(address.getProperties().get("city"));
+        assertNotNull(address.getProperties().get("street"));
+    }
+
+    @Test
+    public void resolveInlineModel2EqualInnerModels() throws Exception {
+        Swagger swagger = new Swagger();
+
+        swagger.addDefinition("User", new ModelImpl()
+                .name("user")
+                .description("a common user")
+                .property("name", new StringProperty())
+                .property("address", new ObjectProperty()
+                        .title("UserAddressTitle")
+                        ._default("default")
+                        .access("access")
+                        .readOnly(false)
+                        .required(true)
+                        .description("description")
+                        .name("name")
+                        .property("street", new StringProperty())
+                        .property("city", new StringProperty())));
+
+        swagger.addDefinition("AnotherUser", new ModelImpl()
+                .name("user")
+                .description("a common user")
+                .property("name", new StringProperty())
+                .property("lastName", new StringProperty())
+                .property("address", new ObjectProperty()
+                        .title("UserAddressTitle")
+                        ._default("default")
+                        .access("access")
+                        .readOnly(false)
+                        .required(true)
+                        .description("description")
+                        .name("name")
+                        .property("street", new StringProperty())
+                        .property("city", new StringProperty())));
+
+        new io.swagger.parser.util.InlineModelResolver().flatten(swagger);
+
+        ModelImpl user = (ModelImpl)swagger.getDefinitions().get("User");
+
+        assertNotNull(user);
+        assertTrue(user.getProperties().get("address") instanceof RefProperty);
+
+        ModelImpl address = (ModelImpl)swagger.getDefinitions().get("UserAddressTitle");
+        assertNotNull(address);
+        assertNotNull(address.getProperties().get("city"));
+        assertNotNull(address.getProperties().get("street"));
+        ModelImpl duplicateAddress = (ModelImpl)swagger.getDefinitions().get("UserAddressTitle_0");
+        assertNull(duplicateAddress);
+    }
+
+    @Test
+    public void resolveInlineModel2DifferentInnerModelsWIthSameTitle() throws Exception {
+        Swagger swagger = new Swagger();
+
+        swagger.addDefinition("User", new ModelImpl()
+                .name("user")
+                .description("a common user")
+                .property("name", new StringProperty())
+                .property("address", new ObjectProperty()
+                        .title("UserAddressTitle")
+                        ._default("default")
+                        .access("access")
+                        .readOnly(false)
+                        .required(true)
+                        .description("description")
+                        .name("name")
+                        .property("street", new StringProperty())
+                        .property("city", new StringProperty())));
+
+        swagger.addDefinition("AnotherUser", new ModelImpl()
+                .name("AnotherUser")
+                .description("a common user")
+                .property("name", new StringProperty())
+                .property("lastName", new StringProperty())
+                .property("address", new ObjectProperty()
+                        .title("UserAddressTitle")
+                        ._default("default")
+                        .access("access")
+                        .readOnly(false)
+                        .required(true)
+                        .description("description")
+                        .name("name")
+                        .property("street", new StringProperty())
+                        .property("city", new StringProperty())
+                        .property("apartment", new StringProperty())));
+
+        new io.swagger.parser.util.InlineModelResolver().flatten(swagger);
+
+        ModelImpl user = (ModelImpl)swagger.getDefinitions().get("User");
+
+        assertNotNull(user);
+        assertTrue(user.getProperties().get("address") instanceof RefProperty);
+
+        ModelImpl address = (ModelImpl)swagger.getDefinitions().get("UserAddressTitle");
+        assertNotNull(address);
+        assertNotNull(address.getProperties().get("city"));
+        assertNotNull(address.getProperties().get("street"));
+        ModelImpl duplicateAddress = (ModelImpl)swagger.getDefinitions().get("UserAddressTitle_1");
+        assertNotNull(duplicateAddress);
+        assertNotNull(duplicateAddress.getProperties().get("city"));
+        assertNotNull(duplicateAddress.getProperties().get("street"));
+        assertNotNull(duplicateAddress.getProperties().get("apartment"));
+    }
+
+
+    @Test
+    public void testInlineResponseModel() throws Exception {
+        Swagger swagger = new Swagger();
+
+        ModelImpl responseSchema = new ModelImpl().type("object").property("name", new StringProperty());
+        responseSchema.setVendorExtension("x-ext", "ext-prop");
+        ModelImpl responseSchemaBaz = new ModelImpl().type("object").property("name", new StringProperty());
+        responseSchemaBaz.setVendorExtension("x-ext", "ext-prop");
+        swagger.path("/foo/bar", new Path()
+                .get(new Operation()
+                        .response(200, new Response()
+                                .description("it works!")
+                                .responseSchema(responseSchema)
+                        )
+                )
+        )
+                .path("/foo/baz", new Path()
+                        .get(new Operation()
+                                .response(200, new Response()
+                                        .vendorExtension("x-foo", "bar")
+                                        .description("it works!")
+                                        .responseSchema(responseSchemaBaz)
+                                )
+                        )
+                );
+        new io.swagger.parser.util.InlineModelResolver().flatten(swagger);
+
+        Map<String, Response> responses = swagger.getPaths().get("/foo/bar").getGet().getResponses();
+
+        Response response = responses.get("200");
+        assertNotNull(response);
+        Property schema = response.getSchema();
+        assertTrue(schema instanceof RefProperty);
+
+        ModelImpl model = (ModelImpl)swagger.getDefinitions().get("inline_response_200");
+        assertTrue(model.getProperties().size() == 1);
+        assertNotNull(model.getProperties().get("name"));
+        assertTrue(model.getProperties().get("name") instanceof StringProperty);
+        assertEquals(1, model.getVendorExtensions().size());
+        assertEquals("ext-prop", model.getVendorExtensions().get("x-ext"));
+    }
+
+
+    @Test
+    public void testInlineResponseModelWithTitle() throws Exception {
+        Swagger swagger = new Swagger();
+
+
+        String responseTitle = "GetBarResponse";
+        ModelImpl responseSchema = new ModelImpl().type("object").property("name", new StringProperty());
+        responseSchema.setTitle(responseTitle);
+        ModelImpl responseSchemaBaz = new ModelImpl().type("object").property("name", new StringProperty());
+
+        swagger.path("/foo/bar", new Path()
+                .get(new Operation()
+                        .response(200, new Response()
+                                .description("it works!")
+                                .responseSchema(responseSchema))))
+                .path("/foo/baz", new Path()
+                        .get(new Operation()
+                                .response(200, new Response()
+                                        .vendorExtension("x-foo", "bar")
+                                        .description("it works!")
+                                        .responseSchema(responseSchemaBaz))));
+        new io.swagger.parser.util.InlineModelResolver().flatten(swagger);
+
+        Map<String, Response> responses = swagger.getPaths().get("/foo/bar").getGet().getResponses();
+
+        Response response = responses.get("200");
+        assertNotNull(response);
+        assertTrue(response.getSchema() instanceof RefProperty);
+
+        ModelImpl model = (ModelImpl)swagger.getDefinitions().get(responseTitle);
+        assertTrue(model.getProperties().size() == 1);
+        assertNotNull(model.getProperties().get("name"));
+        assertTrue(model.getProperties().get("name") instanceof StringProperty);
+    }
+
+
+    @Test
+    public void resolveInlineArrayModelWithTitle() throws Exception {
+        Swagger swagger = new Swagger();
+
+        swagger.addDefinition("User", new ArrayModel()
+                .items(new ObjectProperty()
+                        .title("InnerUserTitle")
+                        ._default("default")
+                        .access("access")
+                        .readOnly(false)
+                        .required(true)
+                        .description("description")
+                        .name("name")
+                        .property("street", new StringProperty())
+                        .property("city", new StringProperty())));
+
+        new io.swagger.parser.util.InlineModelResolver().flatten(swagger);
+
+        Model model = swagger.getDefinitions().get("User");
+        assertTrue(model instanceof ArrayModel);
+
+        Model user = swagger.getDefinitions().get("InnerUserTitle");
+        assertNotNull(user);
+        assertEquals("description", user.getDescription());
+    }
+
+    @Test
+    public void resolveInlineArrayModelWithoutTitle() throws Exception {
+        Swagger swagger = new Swagger();
+
+        swagger.addDefinition("User", new ArrayModel()
+                .items(new ObjectProperty()
+                        ._default("default")
+                        .access("access")
+                        .readOnly(false)
+                        .required(true)
+                        .description("description")
+                        .name("name")
+                        .property("street", new StringProperty())
+                        .property("city", new StringProperty())));
+
+        new io.swagger.parser.util.InlineModelResolver().flatten(swagger);
+
+        Model model = swagger.getDefinitions().get("User");
+        assertTrue(model instanceof ArrayModel);
+
+        Model user = swagger.getDefinitions().get("User_inner");
+        assertNotNull(user);
+        assertEquals("description", user.getDescription());
+    }
+
+
+    @Test
+    public void resolveInlineBodyParameter() throws Exception {
+        Swagger swagger = new Swagger();
+
+        swagger.path("/hello", new Path()
+                .get(new Operation()
+                        .parameter(new BodyParameter()
+                                .name("body")
+                                .schema(new ModelImpl()
+                                        .property("address", new ObjectProperty()
+                                                .property("street", new StringProperty()))
+                                        .property("name", new StringProperty())))));
+
+        new io.swagger.parser.util.InlineModelResolver().flatten(swagger);
+
+        Operation operation = swagger.getPaths().get("/hello").getGet();
+        BodyParameter bp = (BodyParameter)operation.getParameters().get(0);
+        assertTrue(bp.getSchema() instanceof RefModel);
+
+        Model body = swagger.getDefinitions().get("body");
+        assertTrue(body instanceof ModelImpl);
+
+        ModelImpl impl = (ModelImpl) body;
+        assertNotNull(impl.getProperties().get("address"));
+    }
+
+    @Test
+    public void resolveInlineBodyParameterWithRequired() throws Exception {
+        Swagger swagger = new Swagger();
+
+        swagger.path("/hello", new Path()
+                .get(new Operation()
+                        .parameter(new BodyParameter()
+                                .name("body")
+                                .schema(new ModelImpl()
+                                        .property("address", new ObjectProperty()
+                                                .property("street", new StringProperty()
+                                                        .required(true))
+                                                .required(true))
+                                        .property("name", new StringProperty())))));
+
+        new io.swagger.parser.util.InlineModelResolver().flatten(swagger);
+
+        Operation operation = swagger.getPaths().get("/hello").getGet();
+        BodyParameter bp = (BodyParameter)operation.getParameters().get(0);
+        assertTrue(bp.getSchema() instanceof RefModel);
+
+        Model body = swagger.getDefinitions().get("body");
+        assertTrue(body instanceof ModelImpl);
+
+        ModelImpl impl = (ModelImpl) body;
+        assertNotNull(impl.getProperties().get("address"));
+
+        Property addressProperty = impl.getProperties().get("address");
+        assertTrue(addressProperty instanceof RefProperty);
+        assertTrue(addressProperty.getRequired());
+
+        Model helloAddress = swagger.getDefinitions().get("hello_address");
+        assertTrue(helloAddress instanceof ModelImpl);
+
+        ModelImpl addressImpl = (ModelImpl) helloAddress;
+        assertNotNull(addressImpl);
+
+        Property streetProperty = addressImpl.getProperties().get("street");
+        assertTrue(streetProperty instanceof  StringProperty);
+        assertTrue(streetProperty.getRequired());
+    }
+
+    @Test
+    public void resolveInlineBodyParameterWithTitle() throws Exception {
+        Swagger swagger = new Swagger();
+
+        ModelImpl addressModelItem = new ModelImpl();
+        String addressModelName = "DetailedAddress";
+        addressModelItem.setTitle(addressModelName);
+        swagger.path("/hello", new Path()
+                .get(new Operation()
+                        .parameter(new BodyParameter()
+                                .name("body")
+                                .schema(addressModelItem
+                                        .property("address", new ObjectProperty()
+                                                .property("street", new StringProperty()))
+                                        .property("name", new StringProperty())))));
+
+        new io.swagger.parser.util.InlineModelResolver().flatten(swagger);
+
+        Operation operation = swagger.getPaths().get("/hello").getGet();
+        BodyParameter bp = (BodyParameter)operation.getParameters().get(0);
+        assertTrue(bp.getSchema() instanceof RefModel);
+
+        Model body = swagger.getDefinitions().get(addressModelName);
+        assertTrue(body instanceof ModelImpl);
+
+        ModelImpl impl = (ModelImpl) body;
+        assertNotNull(impl.getProperties().get("address"));
+    }
+
+    @Test
+    public void notResolveNonModelBodyParameter() throws Exception {
+        Swagger swagger = new Swagger();
+
+        swagger.path("/hello", new Path()
+                .get(new Operation()
+                        .parameter(new BodyParameter()
+                                .name("body")
+                                .schema(new ModelImpl()
+                                        .type("string")
+                                        .format("binary")))));
+
+        new io.swagger.parser.util.InlineModelResolver().flatten(swagger);
+
+        Operation operation = swagger.getPaths().get("/hello").getGet();
+        BodyParameter bp = (BodyParameter)operation.getParameters().get(0);
+        assertTrue(bp.getSchema() instanceof ModelImpl);
+        ModelImpl m = (ModelImpl) bp.getSchema();
+        assertEquals("string", m.getType());
+        assertEquals("binary", m.getFormat());
+    }
+
+    @Test
+    public void resolveInlineArrayBodyParameter() throws Exception {
+        Swagger swagger = new Swagger();
+
+        swagger.path("/hello", new Path()
+                .get(new Operation()
+                        .parameter(new BodyParameter()
+                                .name("body")
+                                .schema(new ArrayModel()
+                                        .items(new ObjectProperty()
+                                                .property("address", new ObjectProperty()
+                                                        .property("street", new StringProperty())))))));
+
+        new io.swagger.parser.util.InlineModelResolver().flatten(swagger);
+
+        Parameter param = swagger.getPaths().get("/hello").getGet().getParameters().get(0);
+        assertTrue(param instanceof BodyParameter);
+
+        BodyParameter bp = (BodyParameter) param;
+        Model schema = bp.getSchema();
+
+        assertTrue(schema instanceof ArrayModel);
+
+        ArrayModel am = (ArrayModel) schema;
+        Property inner = am.getItems();
+        assertTrue(inner instanceof RefProperty);
+
+        RefProperty rp = (RefProperty) inner;
+
+        assertEquals(rp.getType(), "ref");
+        assertEquals(rp.get$ref(), "#/definitions/body");
+        assertEquals(rp.getSimpleRef(), "body");
+
+        Model inline = swagger.getDefinitions().get("body");
+        assertNotNull(inline);
+        assertTrue(inline instanceof ModelImpl);
+        ModelImpl impl = (ModelImpl) inline;
+        RefProperty rpAddress = (RefProperty) impl.getProperties().get("address");
+        assertNotNull(rpAddress);
+        assertEquals(rpAddress.getType(), "ref");
+        assertEquals(rpAddress.get$ref(), "#/definitions/hello_address");
+        assertEquals(rpAddress.getSimpleRef(), "hello_address");
+
+        Model inlineProp = swagger.getDefinitions().get("hello_address");
+        assertNotNull(inlineProp);
+        assertTrue(inlineProp instanceof ModelImpl);
+        ModelImpl implProp = (ModelImpl) inlineProp;
+        assertNotNull(implProp.getProperties().get("street"));
+        assertTrue(implProp.getProperties().get("street") instanceof StringProperty);
+    }
+
+    @Test
+    public void resolveInlineArrayResponse() throws Exception {
+        Swagger swagger = new Swagger();
+
+        ArrayProperty schema = new ArrayProperty()
+                .items(new ObjectProperty()
+                        .property("name", new StringProperty())
+                        .vendorExtension("x-ext", "ext-items"))
+                .vendorExtension("x-ext", "ext-prop");
+        swagger.path("/foo/baz", new Path()
+                .get(new Operation()
+                        .response(200, new Response()
+                                .vendorExtension("x-foo", "bar")
+                                .description("it works!")
+                                .schema(schema))));
+
+        new io.swagger.parser.util.InlineModelResolver().flatten(swagger);
+
+        Response response = swagger.getPaths().get("/foo/baz").getGet().getResponses().get("200");
+        assertNotNull(response);
+
+        assertNotNull(response.getSchema());
+        Property responseProperty = response.getSchema();
+
+        // no need to flatten more
+        assertTrue(responseProperty instanceof ArrayProperty);
+
+        ArrayProperty ap = (ArrayProperty) responseProperty;
+        assertEquals(1, ap.getVendorExtensions().size());
+        assertEquals("ext-prop", ap.getVendorExtensions().get("x-ext"));
+
+        Property p = ap.getItems();
+
+        assertNotNull(p);
+
+        RefProperty rp = (RefProperty) p;
+        assertEquals(rp.getType(), "ref");
+        assertEquals(rp.get$ref(), "#/definitions/inline_response_200");
+        assertEquals(rp.getSimpleRef(), "inline_response_200");
+        assertEquals(1, rp.getVendorExtensions().size());
+        assertEquals("ext-items", rp.getVendorExtensions().get("x-ext"));
+
+        Model inline = swagger.getDefinitions().get("inline_response_200");
+        assertNotNull(inline);
+        assertTrue(inline instanceof ModelImpl);
+        ModelImpl impl = (ModelImpl) inline;
+        assertNotNull(impl.getProperties().get("name"));
+        assertTrue(impl.getProperties().get("name") instanceof StringProperty);
+    }
+
+    @Test
+    public void resolveInlineArrayResponseWithTitle() throws Exception {
+        Swagger swagger = new Swagger();
+
+        swagger.path("/foo/baz", new Path()
+                .get(new Operation()
+                        .response(200, new Response()
+                                .vendorExtension("x-foo", "bar")
+                                .description("it works!")
+                                .schema(new ArrayProperty()
+                                        .items(new ObjectProperty()
+                                                .title("FooBar")
+                                                .property("name", new StringProperty()))))));
+
+        new io.swagger.parser.util.InlineModelResolver().flatten(swagger);
+
+        Response response = swagger.getPaths().get("/foo/baz").getGet().getResponses().get("200");
+        assertNotNull(response);
+
+        assertNotNull(response.getSchema());
+        Property responseProperty = response.getSchema();
+
+        // no need to flatten more
+        assertTrue(responseProperty instanceof ArrayProperty);
+
+        ArrayProperty ap = (ArrayProperty) responseProperty;
+        Property p = ap.getItems();
+
+        assertNotNull(p);
+
+        RefProperty rp = (RefProperty) p;
+        assertEquals(rp.getType(), "ref");
+        assertEquals(rp.get$ref(), "#/definitions/"+ "FooBar");
+        assertEquals(rp.getSimpleRef(), "FooBar");
+
+        Model inline = swagger.getDefinitions().get("FooBar");
+        assertNotNull(inline);
+        assertTrue(inline instanceof ModelImpl);
+        ModelImpl impl = (ModelImpl) inline;
+        assertNotNull(impl.getProperties().get("name"));
+        assertTrue(impl.getProperties().get("name") instanceof StringProperty);
+    }
+
+    @Test
+    public void testInlineMapResponse() throws Exception {
+        Swagger swagger = new Swagger();
+
+        MapProperty schema = new MapProperty();
+        schema.setAdditionalProperties(new StringProperty());
+        schema.setVendorExtension("x-ext", "ext-prop");
+
+        swagger.path("/foo/baz", new Path()
+                .get(new Operation()
+                        .response(200, new Response()
+                                .vendorExtension("x-foo", "bar")
+                                .description("it works!")
+                                .schema(schema))));
+        new io.swagger.parser.util.InlineModelResolver().flatten(swagger);
+        Json.prettyPrint(swagger);
+
+        Response response = swagger.getPaths().get("/foo/baz").getGet().getResponses().get("200");
+
+        Property property = response.getSchema();
+        assertTrue(property instanceof MapProperty);
+        assertTrue(swagger.getDefinitions().size() == 0);
+        assertEquals(1, property.getVendorExtensions().size());
+        assertEquals("ext-prop", property.getVendorExtensions().get("x-ext"));
+    }
+
+    @Test
+    public void testInlineMapResponseWithObjectProperty() throws Exception {
+        Swagger swagger = new Swagger();
+
+        MapProperty schema = new MapProperty();
+        schema.setAdditionalProperties(new ObjectProperty()
+                .property("name", new StringProperty()));
+        schema.setVendorExtension("x-ext", "ext-prop");
+
+        swagger.path("/foo/baz", new Path()
+                .get(new Operation()
+                        .response(200, new Response()
+                                .vendorExtension("x-foo", "bar")
+                                .description("it works!")
+                                .schema(schema))));
+        new io.swagger.parser.util.InlineModelResolver().flatten(swagger);
+
+        Response response = swagger.getPaths().get("/foo/baz").getGet().getResponses().get("200");
+        Property property = response.getSchema();
+        assertTrue(property instanceof MapProperty);
+        assertEquals(1, property.getVendorExtensions().size());
+        assertEquals("ext-prop", property.getVendorExtensions().get("x-ext"));
+        assertTrue(swagger.getDefinitions().size() == 1);
+
+        Model inline = swagger.getDefinitions().get("inline_response_200");
+        assertTrue(inline instanceof ModelImpl);
+        ModelImpl impl = (ModelImpl) inline;
+        assertNotNull(impl.getProperties().get("name"));
+        assertTrue(impl.getProperties().get("name") instanceof StringProperty);
+    }
+
+    @Test
+    public void testArrayResponse() {
+        Swagger swagger = new Swagger();
+
+        ArrayProperty schema = new ArrayProperty();
+        schema.setItems(new ObjectProperty()
+                .property("name", new StringProperty()));
+
+        swagger.path("/foo/baz", new Path()
+                .get(new Operation()
+                        .response(200, new Response()
+                                .vendorExtension("x-foo", "bar")
+                                .description("it works!")
+                                .schema(schema))));
+        new io.swagger.parser.util.InlineModelResolver().flatten(swagger);
+
+        Response response = swagger.getPaths().get("/foo/baz").getGet().getResponses().get("200");
+        assertTrue(response.getSchema() instanceof ArrayProperty);
+
+        ArrayProperty am = (ArrayProperty) response.getSchema();
+        Property items = am.getItems();
+        assertTrue(items instanceof RefProperty);
+        RefProperty rp = (RefProperty) items;
+        assertEquals(rp.getType(), "ref");
+        assertEquals(rp.get$ref(), "#/definitions/inline_response_200");
+        assertEquals(rp.getSimpleRef(), "inline_response_200");
+
+        Model inline = swagger.getDefinitions().get("inline_response_200");
+        assertTrue(inline instanceof ModelImpl);
+        ModelImpl impl = (ModelImpl) inline;
+        assertNotNull(impl.getProperties().get("name"));
+        assertTrue(impl.getProperties().get("name") instanceof StringProperty);
+    }
+
+    @Test
+    public void testBasicInput() {
+        Swagger swagger = new Swagger();
+
+        ModelImpl user = new ModelImpl()
+                .property("name", new StringProperty());
+
+        swagger.path("/foo/baz", new Path()
+                .post(new Operation()
+                        .parameter(new BodyParameter()
+                                .name("myBody")
+                                .schema(new RefModel("User")))));
+
+        swagger.addDefinition("User", user);
+
+        new io.swagger.parser.util.InlineModelResolver().flatten(swagger);
+
+        Json.prettyPrint(swagger);
+    }
+
+    @Test
+    public void testArbitraryObjectBodyParam() {
+        Swagger swagger = new Swagger();
+
+        swagger.path("/hello", new Path()
+                .get(new Operation()
+                        .parameter(new BodyParameter()
+                                .name("body")
+                                .schema(new ModelImpl()))));
+
+        new io.swagger.parser.util.InlineModelResolver().flatten(swagger);
+
+        Operation operation = swagger.getPaths().get("/hello").getGet();
+        BodyParameter bp = (BodyParameter)operation.getParameters().get(0);
+        assertTrue(bp.getSchema() instanceof ModelImpl);
+        ModelImpl m = (ModelImpl) bp.getSchema();
+        assertNull(m.getType());
+    }
+
+    @Test
+    public void testArbitraryObjectBodyParamInline() {
+        Swagger swagger = new Swagger();
+
+        swagger.path("/hello", new Path()
+                .get(new Operation()
+                        .parameter(new BodyParameter()
+                                .name("body")
+                                .schema(new ModelImpl()
+                                        .property("arbitrary", new ObjectProperty())))));
+
+        new io.swagger.parser.util.InlineModelResolver().flatten(swagger);
+
+        Operation operation = swagger.getPaths().get("/hello").getGet();
+        BodyParameter bp = (BodyParameter)operation.getParameters().get(0);
+        assertTrue(bp.getSchema() instanceof RefModel);
+
+        Model body = swagger.getDefinitions().get("body");
+        assertTrue(body instanceof ModelImpl);
+
+        ModelImpl impl = (ModelImpl) body;
+        Property p = impl.getProperties().get("arbitrary");
+        assertNotNull(p);
+        assertTrue(p instanceof ObjectProperty);
+    }
+
+    @Test
+    public void testArbitraryObjectBodyParamWithArray() {
+        Swagger swagger = new Swagger();
+
+        swagger.path("/hello", new Path()
+                .get(new Operation()
+                        .parameter(new BodyParameter()
+                                .name("body")
+                                .schema(new ArrayModel()
+                                        .items(new ObjectProperty())))));
+
+        new io.swagger.parser.util.InlineModelResolver().flatten(swagger);
+
+        Parameter param = swagger.getPaths().get("/hello").getGet().getParameters().get(0);
+        assertTrue(param instanceof BodyParameter);
+
+        BodyParameter bp = (BodyParameter) param;
+        Model schema = bp.getSchema();
+
+        assertTrue(schema instanceof ArrayModel);
+
+        ArrayModel am = (ArrayModel) schema;
+        Property inner = am.getItems();
+        assertTrue(inner instanceof ObjectProperty);
+
+        ObjectProperty op = (ObjectProperty) inner;
+        assertNotNull(op);
+        assertNull(op.getProperties());
+    }
+
+    @Test
+    public void testArbitraryObjectBodyParamArrayInline() {
+        Swagger swagger = new Swagger();
+
+        swagger.path("/hello", new Path()
+                .get(new Operation()
+                        .parameter(new BodyParameter()
+                                .name("body")
+                                .schema(new ArrayModel()
+                                        .items(new ObjectProperty()
+                                                .property("arbitrary", new ObjectProperty()))))));
+
+        new io.swagger.parser.util.InlineModelResolver().flatten(swagger);
+
+        Parameter param = swagger.getPaths().get("/hello").getGet().getParameters().get(0);
+        assertTrue(param instanceof BodyParameter);
+
+        BodyParameter bp = (BodyParameter) param;
+        Model schema = bp.getSchema();
+
+        assertTrue(schema instanceof ArrayModel);
+
+        ArrayModel am = (ArrayModel) schema;
+        Property inner = am.getItems();
+        assertTrue(inner instanceof RefProperty);
+
+        RefProperty rp = (RefProperty) inner;
+
+        assertEquals(rp.getType(), "ref");
+        assertEquals(rp.get$ref(), "#/definitions/body");
+        assertEquals(rp.getSimpleRef(), "body");
+
+        Model inline = swagger.getDefinitions().get("body");
+        assertNotNull(inline);
+        assertTrue(inline instanceof ModelImpl);
+        ModelImpl impl = (ModelImpl) inline;
+        Property p = impl.getProperties().get("arbitrary");
+        assertNotNull(p);
+        assertTrue(p instanceof ObjectProperty);
+    }
+
+    @Test
+    public void testArbitraryObjectResponse() {
+        Swagger swagger = new Swagger();
+
+        swagger.path("/foo/bar", new Path()
+                .get(new Operation()
+                        .response(200, new Response()
+                                .description("it works!")
+                                .schema(new ObjectProperty()))));
+        new io.swagger.parser.util.InlineModelResolver().flatten(swagger);
+
+        Map<String, Response> responses = swagger.getPaths().get("/foo/bar").getGet().getResponses();
+
+        Response response = responses.get("200");
+        assertNotNull(response);
+        assertTrue(response.getSchema() instanceof ObjectProperty);
+        ObjectProperty op = (ObjectProperty) response.getSchema();
+        assertNull(op.getProperties());
+    }
+
+    @Test
+    public void testArbitraryObjectResponseArray() {
+        Swagger swagger = new Swagger();
+
+        swagger.path("/foo/baz", new Path()
+                .get(new Operation()
+                        .response(200, new Response()
+                                .description("it works!")
+                                .schema(new ArrayProperty()
+                                        .items(new ObjectProperty())))));
+        new io.swagger.parser.util.InlineModelResolver().flatten(swagger);
+
+        Response response = swagger.getPaths().get("/foo/baz").getGet().getResponses().get("200");
+        assertTrue(response.getSchema() instanceof ArrayProperty);
+
+        ArrayProperty am = (ArrayProperty) response.getSchema();
+        Property items = am.getItems();
+        assertTrue(items instanceof ObjectProperty);
+        ObjectProperty op = (ObjectProperty) items;
+        assertNull(op.getProperties());
+    }
+
+    @Test
+    public void testArbitraryObjectResponseArrayInline() {
+        Swagger swagger = new Swagger();
+
+        swagger.path("/foo/baz", new Path()
+                .get(new Operation()
+                        .response(200, new Response()
+                                .vendorExtension("x-foo", "bar")
+                                .description("it works!")
+                                .schema(new ArrayProperty()
+                                        .items(new ObjectProperty()
+                                                .property("arbitrary", new ObjectProperty()))))));
+
+        new io.swagger.parser.util.InlineModelResolver().flatten(swagger);
+
+        Response response = swagger.getPaths().get("/foo/baz").getGet().getResponses().get("200");
+        assertNotNull(response);
+
+        assertNotNull(response.getSchema());
+        Property responseProperty = response.getSchema();
+        assertTrue(responseProperty instanceof ArrayProperty);
+
+        ArrayProperty ap = (ArrayProperty) responseProperty;
+        Property p = ap.getItems();
+        assertNotNull(p);
+
+        RefProperty rp = (RefProperty) p;
+        assertEquals(rp.getType(), "ref");
+        assertEquals(rp.get$ref(), "#/definitions/inline_response_200");
+        assertEquals(rp.getSimpleRef(), "inline_response_200");
+
+        Model inline = swagger.getDefinitions().get("inline_response_200");
+        assertNotNull(inline);
+        assertTrue(inline instanceof ModelImpl);
+        ModelImpl impl = (ModelImpl) inline;
+        Property inlineProp = impl.getProperties().get("arbitrary");
+        assertNotNull(inlineProp);
+        assertTrue(inlineProp instanceof ObjectProperty);
+        ObjectProperty op = (ObjectProperty) inlineProp;
+        assertNull(op.getProperties());
+    }
+
+    @Test
+    public void testArbitraryObjectResponseMapInline() {
+        Swagger swagger = new Swagger();
+
+        MapProperty schema = new MapProperty();
+        schema.setAdditionalProperties(new ObjectProperty());
+
+        swagger.path("/foo/baz", new Path()
+                .get(new Operation()
+                        .response(200, new Response()
+                                .description("it works!")
+                                .schema(schema))));
+        new io.swagger.parser.util.InlineModelResolver().flatten(swagger);
+
+        Response response = swagger.getPaths().get("/foo/baz").getGet().getResponses().get("200");
+
+        Property property = response.getSchema();
+        assertTrue(property instanceof MapProperty);
+        assertTrue(swagger.getDefinitions().size() == 0);
+        Property inlineProp = ((MapProperty) property).getAdditionalProperties();
+        assertTrue(inlineProp instanceof ObjectProperty);
+        ObjectProperty op = (ObjectProperty) inlineProp;
+        assertNull(op.getProperties());
+    }
+
+    @Test
+    public void testArbitraryObjectModelInline() {
+        Swagger swagger = new Swagger();
+
+        swagger.addDefinition("User", new ModelImpl()
+                .name("user")
+                .description("a common user")
+                .property("name", new StringProperty())
+                .property("arbitrary", new ObjectProperty()
+                        .title("title")
+                        ._default("default")
+                        .access("access")
+                        .readOnly(false)
+                        .required(true)
+                        .description("description")
+                        .name("name")));
+
+        new io.swagger.parser.util.InlineModelResolver().flatten(swagger);
+
+        ModelImpl user = (ModelImpl)swagger.getDefinitions().get("User");
+        assertNotNull(user);
+        Property inlineProp = user.getProperties().get("arbitrary");
+        assertTrue(inlineProp instanceof ObjectProperty);
+        ObjectProperty op = (ObjectProperty) inlineProp;
+        assertNull(op.getProperties());
+    }
+
+    @Test
+    public void testArbitraryObjectModelWithArrayInlineWithoutTitle() {
+        Swagger swagger = new Swagger();
+
+        swagger.addDefinition("User", new ArrayModel()
+                .items(new ObjectProperty()
+                        ._default("default")
+                        .access("access")
+                        .readOnly(false)
+                        .required(true)
+                        .description("description")
+                        .name("name")
+                        .property("arbitrary", new ObjectProperty())));
+
+        new io.swagger.parser.util.InlineModelResolver().flatten(swagger);
+
+        Model model = swagger.getDefinitions().get("User");
+        assertTrue(model instanceof ArrayModel);
+        ArrayModel am = (ArrayModel) model;
+        Property inner = am.getItems();
+        assertTrue(inner instanceof RefProperty);
+
+        ModelImpl userInner = (ModelImpl)swagger.getDefinitions().get("User_inner");
+        assertNotNull(userInner);
+        Property inlineProp = userInner.getProperties().get("arbitrary");
+        assertTrue(inlineProp instanceof ObjectProperty);
+        ObjectProperty op = (ObjectProperty) inlineProp;
+        assertNull(op.getProperties());
+    }
+
+    @Test
+    public void testArbitraryObjectModelWithArrayInlineWithTitle() {
+        Swagger swagger = new Swagger();
+
+        swagger.addDefinition("User", new ArrayModel()
+                .items(new ObjectProperty()
+                        .title("InnerUserTitle")
+                        ._default("default")
+                        .access("access")
+                        .readOnly(false)
+                        .required(true)
+                        .description("description")
+                        .name("name")
+                        .property("arbitrary", new ObjectProperty())));
+
+        new io.swagger.parser.util.InlineModelResolver().flatten(swagger);
+
+        Model model = swagger.getDefinitions().get("User");
+        assertTrue(model instanceof ArrayModel);
+        ArrayModel am = (ArrayModel) model;
+        Property inner = am.getItems();
+        assertTrue(inner instanceof RefProperty);
+
+        ModelImpl userInner = (ModelImpl)swagger.getDefinitions().get("InnerUserTitle");
+        assertNotNull(userInner);
+        Property inlineProp = userInner.getProperties().get("arbitrary");
+        assertTrue(inlineProp instanceof ObjectProperty);
+        ObjectProperty op = (ObjectProperty) inlineProp;
+        assertNull(op.getProperties());
+    }
+
+    @Test
+    public void testEmptyExampleOnStrinngTypeModels() {
+        Swagger swagger = new Swagger();
+
+        RefProperty refProperty = new RefProperty();
+        refProperty.set$ref("#/definitions/Test");
+
+        swagger.path("/hello", new Path()
+                .get(new Operation()
+                        .response(200, new Response()
+                                .schema(new ArrayProperty()
+                                        .items(refProperty)))));
+
+        swagger.addDefinition("Test", new ModelImpl()
+                .example(StringUtils.EMPTY)
+                .type("string"));
+        new io.swagger.parser.util.InlineModelResolver().flatten(swagger);
+    }
+}
diff --git a/modules/swagger-parser/src/test/resources/flatten.json b/modules/swagger-parser/src/test/resources/flatten.json
new file mode 100644
index 00000000..dbbb4ddc
--- /dev/null
+++ b/modules/swagger-parser/src/test/resources/flatten.json
@@ -0,0 +1,26 @@
+{
+  "swagger" : "2.0",
+  "definitions" : {
+    "User" : {
+      "required" : [ "address" ],
+      "properties" : {
+        "name" : {
+          "type" : "string"
+        },
+        "address" : {
+          "type" : "object",
+          "description" : "description",
+          "properties" : {
+            "city" : {
+              "type" : "string"
+            },
+            "street" : {
+              "type" : "string"
+            }
+          }
+        }
+      },
+      "description" : "a common user"
+    }
+  }
+}