Using Marvin.JsonPatch and Swashbuckle to Generate Swagger Documents

We have been working to implement new API documentation.  Since ASP.net help pages are not going to be available in .net core we decided to move to Swagger.  In case you aren’t familiar with the pipeline, here are the basics. 

ASP.net help pages were generated from the .net source code.  Swagger documentation is generated from a JSON document.  Swashbuckle is the glue that generates the Swagger JSON from the .net code.  It’s beyond the scope of this document to talk about how awesome this stack is, but you should check it out. 

JSON Patch Documents

We are using the JSON patch documents to allow partial updates to our system.  Since parsing of these documents is not built into ASP.net by default, we are using the Marvin.JsonPatch nuget package.  This is working great for us, but the format of the JsonPatch<T> object that is the parameter to the controller method does not match the format of the JSON payload you have to send to the PATCH request. For example.

public HttpResponseMessage PatchLead([FromUri] int leadId, [FromBody] JsonPatchDocument<PatchLeadViewModel> patch)

The patch parameter has an Operations collection that you can process.  Since this collection is on the object, Swashbuckle generates the sample payload to look like this. 

{
   "Operations": [
     {
       "value": {},
       "path": "string",
       "op": "string",
       "from": "string"
     }
   ]
}

Unfortunately the correct JSON Patch payload looks like this.

[
   { "op": "replace", "path": "/baz", "value": "boo" },
   { "op": "add", "path": "/hello", "value": ["world"] },
   { "op": "remove", "path": "/foo"}
]

The Solution

Swashbuckle gives us some places we can tie into the JSON generation pipeline so we can customize the output.  One of those places is in the SchemaFilter.  SchemaFilters implement the ISchemaFilter interface which has a single method with this signature.

public void Apply(Schema schema, SchemaRegistry schemaRegistry, Type type)

This fires for each of the controller method parameters during the Swagger JSON generation.  You can inspect the type, and if needed override the default output from Swashbuckle.  This is a class that looks for all parameters that implement JsonPatchDocument<T> and replaces the generated output with a simple JSON Patch document example. 

public class JsonPatchDocumentSchemaFilter : ISchemaFilter
{
     public void Apply(Schema schema, SchemaRegistry schemaRegistry, Type type)
     {
         if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(JsonPatchDocument<>))
         {
             schema.example = new[] {new Marvin.JsonPatch.Operations.Operation() {op = "replace", path = "/SamplePath", value = "UpdatedValue"}};
         }
     }
}

The last thing you have to do is register the SchemaFilter with Swashbuckle.  In the startup.cs file where you enable and configure Swashbuckle, you need to add the following.

config.EnableSwagger(c =>
     {
         ...
         c.SchemaFilter<JsonPatchDocumentSchemaFilter>();
     })
     .EnableSwaggerUi(c =>
     {
         ...
     });

Notes

While writing this we were using Marvin.JsonPatch v 0.9.0 and Swashbuckle.Core v5.5.3