1

I need to deserialize the following Json:

{
    "swagger": "2.0",
    "info": {
        "version": "0.0.0",
        "title": "Simple API"
    },
    "paths": {
        "/": {
            "get": {
                "parameters": [
                    {
                        "$ref": "#/parameters/MyParameter"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK",
                        "schema": {
                            "$ref": "#/definitions/MyDefinition"
                        }
                    }
                }
            }
        }
    },
    "parameters": {
        "MyParameter": {
            "name": "MyParameter",
            "in": "query",
            "type": "string"
        }
    },
    "definitions": {
        "MyDefinition": {
            "type": "string"
        }
    }
}

These are the contracts I have defined with Json.NET

    public class SwaggerDocument
    {
        public IDictionary<string, Schema> definitions;
        public Info info;
        public IDictionary<string, Parameter> parameters;
        public IDictionary<string, PathItem> paths;
        public readonly string swagger;
        public Dictionary<string, object> vendorExtensions;
    }

    public class Schema
    {
        [JsonProperty("$ref", IsReference = true)]
        public string @ref;
        public string type;
    }

    public class Info
    {
        public string title;
        public Dictionary<string, object> vendorExtensions;
        public string version;
    }

    public class Parameter
    {
        public string name;
        public string @in;
        public string type;
        public Dictionary<string, object> vendorExtensions;
    }

    public class PathItem
    {
        public Operation get;
        public Dictionary<string, object> vendorExtensions;
    }

    public class Operation
    {
        public IList<Parameter> parameters;
        public IDictionary<string, Response> responses;
    }

    public class Response
    {
        public string description;
        public Schema schema;
    }

I would expect the following to pass but instead, parameter and definition are null:

    [TestMethod]
    public void ShouldDeserializeReferences()
    {
        var swagger = JsonConvert.DeserializeObject<SwaggerDocument>(SwaggerWithRefs);
        var operation = swagger.paths.Values.First().get;
        var parameter = operation.parameters.First();
        var definition = operation.responses.First().Value.schema;
        Assert.AreEqual("MyParameter", parameter.name);
        Assert.AreEqual("string", definition.type);
    }

    private static readonly string SwaggerWithRefs = "{\r\n    \"swagger\": \"2.0\",\r\n    \"info\": {\r\n        \"version\": \"0.0.0\",\r\n        \"title\": \"Simple API\"\r\n    },\r\n    \"paths\": {\r\n        \"/\": {\r\n            \"get\": {\r\n                \"parameters\": [\r\n                    {\r\n                        \"$ref\": \"#/parameters/MyParameter\"\r\n                    }\r\n                ],\r\n                \"responses\": {\r\n                    \"200\": {\r\n                        \"description\": \"OK\",\r\n                        \"schema\": {\r\n                            \"$ref\": \"#/definitions/MyDefinition\"\r\n                        }\r\n                    }\r\n                }\r\n            }\r\n        }\r\n    },\r\n    \"parameters\": {\r\n        \"MyParameter\": {\r\n            \"name\": \"MyParameter\",\r\n            \"in\": \"query\",\r\n            \"type\": \"string\"\r\n        }\r\n    },\r\n    \"definitions\": {\r\n        \"MyDefinition\": {\r\n            \"type\": \"string\"\r\n        }\r\n    }\r\n}";

Is it possible to define the contracts in a way that would make the assertions in the test true? Note that http://editor.swagger.io/ has no problem parsing this json.

EDIT: The referenced answer only mentions deserializing as JObject. Is there a way to get the same behavior with contracts?

Miguel Rivera
  • 78
  • 1
  • 9
  • @adamtickner The referenced answer only mentions deserializing as JObject. Is there a way to get the same behavior with contracts? – Miguel Rivera Sep 23 '16 at 02:08
  • 1
    If you are just looking to deserialize the value of a property named `"$ref"` successfully, see [Can not deserialize JSON containing $ref keys](https://stackoverflow.com/questions/22299390/can-not-deserialize-json-containing-ref-keys). Also you should probably remove `IsReference = true` from `[JsonProperty("$ref", IsReference = true)]` – dbc Sep 23 '16 at 04:42
  • I want Json.NET to go grab the Parameter object in #/parameters/MyParameter and put that object in swagger.paths.Values.First().get.operation.parameters, as per the contract. – Miguel Rivera Sep 23 '16 at 07:02
  • 1
    There is no way to do that out of the box. Json.NET does a single-pass deserialization but your JSON contains a forward reference, the resolution of which would require a second pass. You'll need to load the JSON into a [LINQ to JSON](http://www.newtonsoft.com/json/help/html/LINQtoJSON.htm) hierarchy and translate the Swagger `$ref` into the backward-reference-only [`PreserveReferencesHandling`](http://www.newtonsoft.com/json/help/html/PreserveReferencesHandlingObject.htm) format of Json.NET. – dbc Sep 23 '16 at 13:24
  • I was afraid that would be the case, thanks a lot for the info. – Miguel Rivera Sep 23 '16 at 20:45
  • @MiguelRivera Hi, how did you implement this? I am interested in the solution. Thanks! – Apeksha Dec 21 '17 at 21:57
  • @Apeksha custom implementation. Deserialize the Swagger, then walk the tree, parse the $refs and insert the desired objects. – Miguel Rivera Nov 28 '18 at 22:21

1 Answers1

-1
public class SwaggerDocument
{
    public string host { get; set; }
    public IDictionary<string, IDictionary<string, PathItem>> paths;
}

public class PathItem
{
    public string summary { get; set; }
    public string operationId { get; set; }
    public object parameters { get; set; }
    public object responses { get; set; }
}
Daniel
  • 1
  • 2