Technical Blog

How To Populate Metadata When Converting Markdown to HTML in ASP.NET

Using a JSON Metadata file, a single view and a dynamic action method to pass metadata into Markdown converted HTML pages.

Carl Mills

Published

In a previous blog post, I explained how I used a Markdown to HTML conversion tool to turn our Markdown files into HTML webpages for the Aireforge Studio documentation. In summary, all Markdown files were routed through a single dynamic action method with the markdown filename passed as an input parameter. The action method then extracted the content of the Markdown file, converted it to HTML and used a single view to generate each files content as and when it was called.

Typically, action methods represent one page, and as such any metadata for a given page (title, metadescription, request path) is hard-coded within the action method:

public ActionResult Index()
{
	ViewBag.Title = "SEO Optimized title for your page";
	ViewBag.MetaDescription = "A brief summary to accompany the title on the search engine listing";

	return View();
}

Given that all of our Markdown files are being converted to HTML from one dynamic action method, this called for a different technique.

Creating a JSON Metadata File

There are only a few metadata properties for each page, so a database call seemed unnecessary. In accordance of Microsoft’s progressions within the .NET Core architecture, I opted for an embedded JSON file to hold this data. After considering the data required, I structured the JSON file in the following way:

{
    "MarkdownDocuments": [
        {
            "Type": "Documentation",
            "Documents": [
                {
                    "Filename": "",
                    "Title": "",
                    "MetaDescription": "",
                    "MetaKeywords": "",
                    "SidebarHierarchy": ""
                }
            ]
        },
        {
            "Type": "Blog",
            "Documents": [
                {
                    "Filename": "",
                    "Title": "",
                    "MetaDescription": "",
                    "MetaKeywords": "",
                    "SidebarHierarchy": "",
                    "ArchiveDate": "",
                    "Author": ""
                }
            ]
        }
    ]
}

This covered all properties I wanted to pass into my view. As we also intend to use the Markdown conversion process for blog posts, I prearranged the JSON to allow separation of these Markdown types to speed up metadata selection and readability. This hierarchy also allowed a unified Document class for both Blog and Documentation properties in the MarkdownLookup Model:

public class Document
{
    public string Filename { get; set; }
    public string Title { get; set; }
    public string MetaDescription { get; set; }
    public string MetaKeywords { get; set; }
    public string SidebarHierarchy { get; set; }
    public string ArchiveDate { get; set; }
}

public class MarkdownDocument
{
    public string Type { get; set; }
    public List Documents { get; set; }
}

public class MDLookup
{
    public List MarkdownDocuments { get; set; }
}

Extracting the JSON Property Values

With the JSON file in place, I needed a way to extract the values and pass these into the view for every Markdown file. The PopulateMDLookup method takes the filename (element) and the document type (in this case either Blog or Documentation). This method then reads the JSON file and deserializes it into the MarkdownLookup model we created. A Linq query is then used to refine the full list of each document’s metadata to those that are of the same type (in this case, Documentation) and then isolates the single document by the filename:

internal static  class MarkdownHelper
{
    private static string MarkdownLookupjson = System.IO.File.ReadAllText(AppDomain.CurrentDomain.BaseDirectory + @"\Content\Resources\MarkdownLookup.json");
    private static MDLookup _MDMetaData = JsonConvert.DeserializeObject(MarkdownLookupjson);
    public static Document MD;

    internal static Document PopulateMDLookup(string element, string type)
    {
        string json = System.IO.File.ReadAllText(AppDomain.CurrentDomain.BaseDirectory + @"\Content\Resources\MarkdownLookup.json");

        MarkdownDocument MDDocuments = _MDMetaData.MarkdownDocuments.FirstOrDefault(a => a.Type == type);

        MD = MDDocuments.Documents.FirstOrDefault(a => a.Filename == element);

        return MD;
    }
}

The PopulateMDLookup method is then called by the dynamic action method by using the filename that was already being passed in, along with the type of Markdown file we are looking for. A Document object is returned with all the metadata values we require.

public ActionResult ProcessCommand(string element) //element is the Markdown filename
{
    try
    {
        string MarkdownHtml = RenderHTMLFromMD(@"\Content\MarkdownFiles-Docs\" + element + ".md");

        ViewBag.MarkdownHTML = MarkdownHtml;

        //Calls the config file here
        Document MD = MarkdownHelper.PopulateMDLookup(element, "Documentation");

        //Add each metadata element to the designated ViewBag property
        if (MD != null)
        {
            ViewBag.Title = MD.Title;
            ViewBag.MetaDescription = MD.MetaDescription;
            ViewBag.MetaKeywords = MD.MetaKeywords;
        }

        return View("Index");
    }
    catch(Exception ex)
    {
        Logger.Log(ex);
        return RedirectToAction("NotFound", "Shared");
    }
}

To check the process worked, I inspected the header elements in the browser while running the web app from my local environment:

markdown-json-metadata-result

Get Started With

Start Now
icon_bluestone98