I've noticed a few discrepancies in the rendering of markdown inside swashbuckle/swagger-ui.
The issue stems from the .NET xml comments and how the xml file is produced - each line is usually indented for easier human reading. The leading and trailing whitespace is trimmed, but it leaves any intermediate lines with extra leading whitespace, killing any hopes of an easy markdown conversion.
I've written a javascript workaround script which is added to a custom "index.html". It attempts to calculate the leading padding remove it from each line: https://gist.github.com/davetransom/ddb755a61ad86f0e9690
Though, the xml file could possibly be massaged into a better format via an IDocumentFilter too.
I wasn't sure if I should contribute this as a PR (it would add a .js file to the swashbuckle project), so thought I'd raise an issue for discussion first.
current: partial conversion to html
Note the sentence wrapping and the code block not behaving.
expected:
As an aside, it's worth noting that the swagger-ui version sometimes puts a markdown class on <p> elements, which will lead to a nested <p> situation. The javascript workaround also averts that.
Between 5.0.4 and 5.1.2, the conversion of XML comments to markdown has worsened considerably. para tags used to be converted to p, for example, which allowed for some readability formatting. Now it's rendered as a single paragraph.
I hope this back-step is just temporary while the XmlComment importers are being refactored. For the time being I am sticking with 5.0.4 for applications that have active consumers though.
It would be helpful if one of the Swashbuckle devs could share their thoughts/plans on the documentation comments they plan to support (https://msdn.microsoft.com/en-us/library/5ast78ax(v=vs.120).aspx).
Unless I'm mistaken, I don't believe there has ever been any code that converts para to p tags so not sure how this was ever working.
The currently supported tags are listed in the readme - https://github.com/domaindrivendev/Swashbuckle#including-xml-comments
Looking at the incoming issues, there is definitely a desire out there for richer support but there is also so many hours in the day and other features are higher on the list of priorities right now.
It's worth noting however, that you can easily implement this yourself with a very simple extension point. Just wire up a custom operation filter to post process the generated Swagger document.
For example:
//SwaggerConfig.cs
c.IncludeXmlComments(GetXmlCommentsPath());
c.OperationFilter<FormatXmlCommentProperties>(); // after above line for correct execution order
//FormatXmlCommentProperties.cs
public class FormatXmlCommentProperties : IOperationFilter
{
public void Apply(Operation operation, SchemaRegistry schemaRegistry, ApiDescription apiDescription)
{
operation.description = Formatted(operation.description);
operation.summary = Formatted(operation.summary);
}
private string Formatted(string text)
{
if (text == null) return null;
var stringBuilder = new StringBuilder(text);
return stringBuilder
.Replace("<para>", "<p>")
.Replace("</para>", "</p>")
.ToString();
}
}
At some point in the future, we'll get this built in out of the box - still puzzled as to how this was formatting better previously.
Building on @domaindrivendev 's custom formatting sample above, I found that you can get some pretty decent markdown support in XML Comments by using the following Formatted() method (or something similar to your liking) in your custom FormatXmlCommentProperties.cs class:
``` C#
private string Formatted(string text)
{
if (text == null) return null;
// Strip out the whitespace that messes up the markdown in the xml comments,
// but don't touch the whitespace in <code> blocks. Those get fixed below.
string resultString = Regex.Replace(text, @"(^[ \t]+)(?![^<]*>|[^>]*<\/)", "", RegexOptions.Multiline);
resultString = Regex.Replace(resultString, @"<code[^>]*>", "<pre>", RegexOptions.IgnoreCase | RegexOptions.Singleline | RegexOptions.Multiline);
resultString = Regex.Replace(resultString, @"</code[^>]*>", "</pre>", RegexOptions.IgnoreCase | RegexOptions.Singleline | RegexOptions.Multiline);
resultString = Regex.Replace(resultString, @"<!--", "", RegexOptions.Multiline);
resultString = Regex.Replace(resultString, @"-->", "", RegexOptions.Multiline);
try
{
string pattern = @"<pre\b[^>]*>(.*?)</pre>";
foreach (Match match in Regex.Matches(resultString, pattern, RegexOptions.IgnoreCase | RegexOptions.Singleline | RegexOptions.Multiline ))
{
var formattedPreBlock = FormatPreBlock(match.Value);
resultString = resultString.Replace(match.Value, formattedPreBlock);
}
return resultString;
}
catch
{
// Something went wrong so just return the original resultString
return resultString;
}
}
private string FormatPreBlock(string preBlock)
{
// Split the <pre> block into multiple lines
var linesArray = preBlock.Split('\n');
if (linesArray.Length < 2)
{
return preBlock;
}
else
{
// Get the 1st line after the <pre>
string line = linesArray[1];
int lineLength = line.Length;
string formattedLine = line.TrimStart(' ', '\t');
int paddingLength = lineLength - formattedLine.Length;
// Remove the padding from all of the lines in the <pre> block
for (int i = 1; i < linesArray.Length-1; i++)
{
linesArray[i] = linesArray[i].Substring(paddingLength);
}
var formattedPreBlock = string.Join("", linesArray);
return formattedPreBlock;
}
}
Using this formatter you can have an Xml Comment block in your code that looks like this for example:
``` C#
/// <summary>
/// Gets a the list of entities and their descriptions from the Entity Service.
/// </summary>
/// <remarks>
/// ### REMARKS ###
/// Use this call to retrieve a list of entities and their descriptions. This is used when setting up a new ViewSet.
/// - Returns an array of **EntityDescription** objects.
/// - The array is `ordered alphabetically` by the Name property.
/// <!--
/// <code>
/// // contrived code sample to demonstrate code block in swagger.
/// for (i=0; i < 5; i++)
/// {
/// doesthiswork = yesitdoes;
/// }
/// </code>
/// -->
/// </remarks>
/// <response code="200">OK</response>
/// <returns>An array of EntityDescription objects.</returns>
Which will yield the following output:
Note that you have to comment out the <code> block as shown using the <!-- --> strategy in your XML comments or else the Visual Studio XML compiler will say that it's badly formed and not include it in the output.
Thanks for sharing! It's very helpful.
Thanks Geokaps, I think your on the right course.
We won't be able to do much more without getting at the content as it is loaded from XML (before all the XML documentation tags have been stripped). This happens in the ApplyXmlTypeComments and ApplyXmlActionComments classes in a call to XPathNavigatorExtensiosn.ExtractContent() (typo in original). Since this is a static method there isn't much we can do to affect it's behavior externally.
Just adding a few more thoughts...
I prefer to write _just markdown_ in each content element (albeit XML escaped), so no <para> or <see>, <c>, etc... given its destination/intention is public documentation, and not so much for VS IntelliSense. I also feel plain markdown would give a more consistent transition through to swagger, where it's consumed.
I guess it comes down to how you see Swashbuckle working in your project.
After looking at this more, while the javascript solution worked as a stopgap, I think Swashbuckle should probably have some form of XML comment "padding stripper" built in (perhaps inside XPathNavigatorExtensiosn.ExtractContent) - just to deal with the way VS XML documents are produced (since it's the extra significant whitespace that seems to be causing the issue).
Then perhaps have a separate built-in to "Convert additional elements" similar to what @geokaps has suggested - that can be applied at the configuration level, depending on your preference. Although one thing to be wary of when stripping XML comments e.g. "
Most helpful comment
Building on @domaindrivendev 's custom formatting sample above, I found that you can get some pretty decent markdown support in XML Comments by using the following
Formatted()method (or something similar to your liking) in your customFormatXmlCommentProperties.csclass:``` C#
private string Formatted(string text)
{
if (text == null) return null;
Which will yield the following output:

Note that you have to comment out the
<code>block as shown using the<!-- -->strategy in your XML comments or else the Visual Studio XML compiler will say that it's badly formed and not include it in the output.