Parse Documents to Markdown Chunks in .NET: Integration Guide

Parse Documents to Markdown Chunks in .NET: Integration Guide

Note
💡 Full working example available on GitHub: parse-docx-to-markdown-chunks-dotnet

Overview

Integrating document-to-chunk extraction into a C# service is a GroupDocs.Parser task for .NET that wires inspect, Markdown extract, plain-text fallback, and heading split into an existing host. Embedding a whole multi-page DOCX as one vector ranks the wrong section. I needed this layout after a three-page policy DOCX ranked the cover clause for every query because the embedding covered the whole file.

What this guide covers:

  • Drop-in NuGet setup for GroupDocs.Parser 26.10.0 on .NET 8
  • Four callable patterns: info gate, Markdown extract, PDF GetText, heading chunks
  • Where each pattern sits in a retrieval intake job
  • Production checks for null readers, stale chunk files, and licensing
Tip
Complete source code is available in the GitHub repository. Clone it and run it locally before wiring the methods into your service.

Quickstart

  1. dotnet add package GroupDocs.Parser --version 26.10.0
  2. Point a console or worker project at a DOCX under documents/
  3. Call GetDocumentInfo, then GetFormattedText with FormattedTextMode.Markdown
  4. On null reader, call GetText; when Markdown has # / ##, run the heading split into chunk-NN.md

The shipped sample DOCX reports 3 pages and 14820 bytes and produces four chunk files under Result/chunks/.

How do I plug these four calls into an existing intake job?

Call GetDocumentInfo at the start of each job so page count and size can skip empty or huge uploads. Try GetFormattedText Markdown next for Word DOCX. If the reader is null, invoke GetText and store plain text instead of failing. Run the heading splitter only when ATX markers exist. That sequence fits a queue worker without a second Office converter and matches the sample Program.cs flow.

Integration Pattern 1: Pre-extract gate

Use Parser.GetDocumentInfo when the job must decide skip-or-extract before touching the text layer.

using var parser = new Parser(sourcePath);
var info = parser.GetDocumentInfo();

var sb = new StringBuilder();
sb.AppendLine($"Source: {Path.GetFileName(sourcePath)}");
sb.AppendLine($"File type: {info?.FileType}");
sb.AppendLine($"Pages: {info?.PageCount}");
sb.AppendLine($"Size: {info?.Size} bytes");
File.WriteAllText(outputPath, sb.ToString());

Write the facts next to the job id. Shared prelude for Word and PDF. Does not replace body extraction.

Integration Pattern 2: Markdown extract for Word

Call this when styled headings should become ATX markers for later chunk files.

using var parser = new Parser(sourcePath);
using TextReader? reader = parser.GetFormattedText(
    new FormattedTextOptions(FormattedTextMode.Markdown));

if (reader == null)
{
    return null;
}

string markdown = reader.ReadToEnd();
File.WriteAllText(outputPath, markdown, Encoding.UTF8);
return markdown;

Keep the using scope tight. Prefer this path whenever the reader is non-null. See the formatted text and Markdown docs for mode details.

Integration Pattern 3: PDF plain-text fallback

Wire this branch when Markdown mode returns null so PDF text still reaches the index.

using var parser = new Parser(sourcePath);
using TextReader? reader = parser.GetText();
if (reader == null)
{
    return null;
}

string text = reader.ReadToEnd();
File.WriteAllText(outputPath, text, Encoding.UTF8);
return text;

Output is plain text. Do not assume ATX structure. Surface a null GetText to operators instead of writing an empty success.

Integration Pattern 4: Heading chunk writer

Attach this after a successful Markdown extract when the embedding index needs one file per section.

var chunks = new List<string>();
var current = new StringBuilder();
using (var reader = new StringReader(markdown))
{
    string? line;
    while ((line = reader.ReadLine()) != null)
    {
        bool isHeading = line.StartsWith("# ", StringComparison.Ordinal)
            || line.StartsWith("## ", StringComparison.Ordinal);
        if (isHeading && current.Length > 0)
        {
            chunks.Add(current.ToString().TrimEnd());
            current.Clear();
        }
        current.AppendLine(line);
    }
}

Clear stale chunk-*.md files before writing. Only # and ## start new chunks in this sample; ### stays nested. Return the chunk count for dashboards.

Pattern chooser

Intake situationPattern to call
Unknown size or typePre-extract gate
Word DOCX with headingsMarkdown extract, then heading chunk writer
PDF with null Markdown readerPDF plain-text fallback
Mixed corpusMarkdown first, then GetText on null

When to use this approach

Use this integration when a .NET retrieval pipeline must accept Word and PDF through one Parser dependency and emit section-sized Markdown for embeddings. Skip the heading writer for unstructured PDF text until you add another chunk policy. Prefer a single console or worker host over a separate Open XML converter only for headings.

Common pitfalls

  • Treating a null Markdown reader as a hard failure instead of calling GetText
  • Running the ATX splitter on plain PDF text that has no # lines
  • Leaving old chunk-*.md files so previous sections mix with the new run
  • Extracting overnight without a temporary license while evaluation limits still apply
  • Re-parsing on every chunk retry instead of writing Markdown once and re-splitting from disk

Production checklist

  • Dispose Parser and TextReader on every file
  • Log page count, size, and chunk totals per job
  • Isolate parsing from the public web process
  • Keep sample DOCX/PDF under documents/ for smoke tests
  • Pin GroupDocs.Parser 26.10.0 in the .csproj until you deliberately upgrade

FAQ

Q: Do I need all four patterns in every service?
A: No. Facts-only jobs can stop at GetDocumentInfo. Word-only indexes can skip the PDF branch. Mixed intake should keep Markdown-then-GetText and chunk only when markers exist.

Q: What does the sample DOCX produce?
A: Info for a 3-page, 14820-byte Word Open XML file, a full Markdown extract, and four heading chunk files under Result/chunks/.

Q: Where is the runnable host?
A: parse-docx-to-markdown-chunks-dotnet on GitHub, with the companion blog article.

Next step

Clone the repository, run dotnet run against documents/sample.docx, then copy the four methods into your intake worker and assert chunk counts in CI.

See Also

Close
Loading

Analyzing your prompt, please hold on...

An error occurred while retrieving the results. Please refresh the page and try again.