Analyzing your prompt, please hold on...
An error occurred while retrieving the results. Please refresh the page and try again.
Aspose.HTML for .NET provides a dedicated Markdown parsing API in the
Aspose.Html.Toolkit.Markdown.Syntax namespace. The
MarkdownParser class converts a .md file into a strongly typed MarkdownSyntaxTree – a Markdown Abstract Syntax Tree (AST) that represents the full document structure.
Each Markdown element is mapped to a specific node type:
#, ##, etc.)Unlike text-based Markdown converters, this API gives you direct programmatic access to the document structure before rendering. Instead of converting Markdown to HTML first, you can analyze, validate, filter, or modify the syntax tree at the structural level.
To build a syntax tree:
1using Aspose.Html.Toolkit.Markdown.Syntax.Parser;
2
3var parser = new MarkdownParser();
4var syntaxTree = parser.ParseFile("document.md");syntaxTree is the root node of the Markdown AST and provides access to all child nodes in the document. In the following sections, you will learn how to traverse the syntax tree, extract headings and tables, and filter nodes by type.
Parsing builds the full syntax tree in memory. For very large Markdown files, consider memory usage and processing time when designing batch-processing workflows.
Once the Markdown file is parsed into a MarkdownSyntaxTree, you can traverse its nodes to inspect or process document elements. Traversal begins at
syntaxTree.FirstChild and continues via
NextSibling, following the linked-node structure of the Markdown syntax tree.
Example: iterating through top-level nodes of the document:
1var node = syntaxTree.FirstChild;
2
3while (node != null)
4{
5 // Process node
6 Console.WriteLine(node.GetType().Name);
7
8 node = node.NextSibling;
9}This pattern ensures efficient and safe traversal of the Markdown AST without relying on list indexing.
For more advanced scenarios, such as visiting only specific node types (e.g., tables or headings), you can use CreateTreeWalker() together with a MarkdownSyntaxNodeFilter. This approach enables selective traversal of the syntax tree.
After parsing a Markdown file into a MarkdownSyntaxTree, you can programmatically extract specific node types. This example demonstrates how to locate all ATX-style headings (#, ##, ###), determine their nesting level (H1–H6), and reconstruct their hierarchy using the syntax tree.
1using System.Text;
2using Aspose.Html.Toolkit.Markdown.Syntax;
3using Aspose.Html.Toolkit.Markdown.Syntax.Parser;
4using System.IO;
5...
6
7 // Parse a Markdown file and extract ATX headings (# H1, ## H2, ### H3)
8 // preserving their hierarchy and nesting level using Aspose.HTML for .NET.
9
10 // Initialize the parser and build a syntax tree from the .md file
11 var parser = new MarkdownParser();
12 var syntaxTree = parser.ParseFile("document.md");
13
14 var headings = new List<(int Level, string Text)>();
15
16 // Walk top-level nodes using FirstChild -> NextSibling
17 var topNode = syntaxTree.FirstChild;
18 while (topNode != null)
19 {
20 if (topNode is AtxHeadingSyntaxNode heading)
21 {
22 // Determine heading level (1–6)
23 int level = heading.GetOpeningTag()
24 .ToString()
25 .Count(c => c == '#');
26
27 // Extract heading text from child nodes
28 var sb = new StringBuilder();
29 var child = heading.FirstChild;
30 while (child != null)
31 {
32 if (child is TextSyntaxNode || child is WhitespaceSyntaxNode)
33 sb.Append(child.ToString());
34 child = child.NextSibling;
35 }
36
37 headings.Add((level, sb.ToString().Trim()));
38 }
39
40 topNode = topNode.NextSibling;
41 }
42
43 // Output heading hierarchy with indentation reflecting nesting depth
44 foreach (var (level, text) in headings)
45 {
46 string indent = new string(' ', (level - 1) * 2);
47 string marker = new string('#', level);
48 Console.WriteLine($"{indent}{marker} {text}");
49 }MarkdownParser.ParseFile() builds a MarkdownSyntaxTree, which represents the complete Markdown Abstract Syntax Tree (AST).syntaxTree.FirstChild and continues through NextSibling.# characters. (Setext-style headings are represented by SetextHeadingSyntaxNode and are not processed in this example.)# characters in the opening token defines the nesting depth.After parsing Markdown into a MarkdownSyntaxTree, you can selectively traverse the document tree to locate specific node types. This example demonstrates how to extract GitHub Flavored Markdown (GFM) tables using a TreeWalker together with a custom MarkdownSyntaxNodeFilter. This example extracts serialized Markdown content of each cell.
Unlike manual top-level traversal, this approach walks the entire syntax tree and returns only nodes that match the specified type.
1using System.Text;
2using Aspose.Html.Toolkit.Markdown.Syntax;
3using Aspose.Html.Toolkit.Markdown.Syntax.Parser;
4using System.IO;
5...
6
7 // Parse Markdown and extract GFM tables using TreeWalker
8
9 // Initialize the parser and build a syntax tree from the .md file
10 var parser = new MarkdownParser();
11 var syntaxTree = parser.ParseFile("document.md");
12
13 // Create a TreeWalker that visits only TableSyntaxNode nodes
14 using var tableWalker = syntaxTree.CreateTreeWalker(new TypeFilter<TableSyntaxNode>());
15
16 MarkdownSyntaxNode current;
17 while ((current = tableWalker.NextNode()) != null)
18 {
19 if (current is not TableSyntaxNode table) continue;
20
21 // Iterate rows via FirstChild -> NextSibling
22 var row = table.FirstChild;
23 while (row != null)
24 {
25 if (row is TableRowSyntaxNode tableRow)
26 {
27 var sb = new StringBuilder();
28 var cell = tableRow.FirstChild;
29 while (cell != null)
30 {
31 if (cell is TableCellSyntaxNode)
32 sb.Append($"| {cell.ToString().Trim()} ");
33 cell = cell.NextSibling;
34 }
35 if (sb.Length > 0)
36 Console.WriteLine(sb.Append('|').ToString());
37 }
38 row = row.NextSibling;
39 }
40 }
41
42 // Custom filter that accepts only nodes of type T
43 class TypeFilter<T> : MarkdownSyntaxNodeFilter where T : MarkdownSyntaxNode
44 {
45 public override short AcceptNode(MarkdownSyntaxNode node)
46 => node is T ? (short)1 : (short)3;
47 }MarkdownParser.ParseFile() builds a MarkdownSyntaxTree, representing the full Markdown AST.TypeFilter<TableSyntaxNode> inherits from
MarkdownSyntaxNodeFilter and overrides AcceptNode().1 (FILTER_ACCEPT) includes the node3 (FILTER_SKIP) ignores itFirstChild → NextSibling), since NodeList does not support index access.cell.ToString().Trim() returns the serialized Markdown content of the cell, including inline formatting.MarkdownSyntaxTree is mutable. You can traverse nodes, modify their content, replace children, and save the updated Markdown document.
The following code modifies Markdown emphasis nodes in a syntax tree using Aspose.HTML for .NET. It replaces all occurrences of a deprecated product name (Aspose.HTML) with a new one (Aspose.HTML for .NET) – directly via the syntax tree, without using regex or string replacement.
1using Aspose.Html.Toolkit.Markdown.Syntax;
2using Aspose.Html.Toolkit.Markdown.Syntax.Parser;
3using System.IO;
4...
5
6 // Replace text inside Emphasis nodes using Markdown DOM in C#
7
8 // Parse Markdown string into a full syntax tree
9 string markdownContent =
10 "# Release Notes\n\n" +
11 "*Aspose.HTML* is a powerful HTML processing library.\n" +
12 "Use *Aspose.HTML* to convert, parse, and manipulate HTML documents.\n" +
13 "The latest version of *Aspose.HTML* includes Markdown support.";
14
15 var parser = new MarkdownParser();
16 var markdown = parser.Parse(markdownContent);
17
18 // SyntaxFactory is used to create new Markdown nodes
19 var factory = markdown.SyntaxFactory;
20
21 int replacementCount = 0;
22
23 // Recursive traversal of the Markdown syntax tree
24 void TraverseAndReplace(MarkdownSyntaxNode node)
25 {
26 // Detect inline emphasis nodes (e.g., *text*)
27 if (node is EmphasisSyntaxNode emphasisNode)
28 {
29 // Ensure emphasis contains exactly one text node
30 if (emphasisNode.FirstChild is TextSyntaxNode textNode &&
31 emphasisNode.FirstChild.NextSibling == null)
32 {
33 // Extract plain text from TextSyntaxNode
34 string currentText = textNode.ToString().Trim();
35
36 // Perform semantic comparison instead of raw string replacement
37 if (currentText == "Aspose.HTML")
38 {
39 // Remove old text node
40 emphasisNode.RemoveChild(textNode);
41
42 // Insert new text node using SyntaxFactory
43 emphasisNode.AppendChild(
44 factory.Text("Aspose.HTML for .NET")
45 );
46 replacementCount++;
47 }
48 }
49 }
50
51 // Pointer-based traversal (FirstChild / NextSibling pattern)
52 var child = node.FirstChild;
53 while (child != null)
54 {
55 var next = child.NextSibling;
56 TraverseAndReplace(child);
57 child = next;
58 }
59 }
60
61 // Start AST traversal from the root Markdown document node
62 TraverseAndReplace(markdown);
63
64 // Output statistics for debugging or logging
65 Output.WriteLine($"Replaced {replacementCount} occurrence(s).");
66
67 // Save modified Markdown file
68 markdown.Save(Path.Combine(OutputDir, "modified-release-notes.md"));How to parse a Markdown file into a syntax tree?
1var syntaxTree = new MarkdownParser().ParseFile("document.md");Builds a MarkdownSyntaxTree – the root of the sintax tree that represents the full document structure.
How to iterate top-level nodes in the Markdown AST?
1var node = syntaxTree.FirstChild;
2while (node != null)
3{
4 /* process node */
5 node = node.NextSibling;
6}NodeList returned by ChildNodes() does not support index access. Use pointer-based traversal: FirstChild → NextSibling.
How to check whether a node is a heading?
1if (node is AtxHeadingSyntaxNode heading)
2{
3 /* H1–H6 heading */
4}AtxHeadingSyntaxNode represents ATX-style headings (#, ##, … ######) in the Markdown syntax tree.
How to traverse only specific node types using a filter?
1using var walker = syntaxTree.CreateTreeWalker(new TypeFilter<TableSyntaxNode>());
2
3while (walker.NextNode() != null)
4{
5 /* only TableSyntaxNode nodes */
6}TypeFilter<T> is a custom MarkdownSyntaxNodeFilter that accepts only nodes of type T, enabling selective traversal of the document tree.
Q: What is the difference between AtxHeadingSyntaxNode and SetextHeadingSyntaxNode?
AtxHeadingSyntaxNode represents headings defined with leading # characters (# H1, ## H2). SetextHeadingSyntaxNode represents headings underlined with = or - on the next line. Both are valid Markdown heading styles, but they map to different node classes in the syntax tree.
Q: Why does NodeList not support index access like children[0]?
NodeList in the Aspose.HTML Markdown API does not implement IList<T> and has no indexer. The correct traversal pattern is pointer-based: start from FirstChild and
advance through siblings using NextSibling until the value is null.
Q: Does TreeWalker traverse the entire document or only direct children?
CreateTreeWalker(syntaxTree.FirstChild) starts traversal from the first child node and visits only its descendants. To traverse the entire document, pass a
MarkdownSyntaxNodeFilter directly: CreateTreeWalker(filter) – this starts from the tree root and visits all nodes.
In this guide, you learned how to:
Analyzing your prompt, please hold on...
An error occurred while retrieving the results. Please refresh the page and try again.