{"slug": "decompiling-an-excel-template-into-source-1-c", "title": "Decompiling an Excel template into source - #1 C#", "summary": "Kookerella Ltd. released a .NET 10 file-based app and CLI tool, fsopenxmldsl-mcp, that decompiles Excel templates into C# source code, enabling developers to reproduce spreadsheet styling programmatically. The tool, available as a dotnet global tool, converts an .xlsx file into a runnable .cs file that regenerates the original spreadsheet pixel-for-pixel, including styles and formulas, and the company demonstrated the workflow in a blog post with a sample invoice template.", "body_md": "# Decompiling an Excel template into source - #1 C#\n\nYou don’t have to hand-write Excel styling code. If someone already built the spreadsheet\nyou need to reproduce — a finance person’s invoice template, a report layout a designer\nsigned off on — you can reverse-engineer it into real source code, then wire in your own\ndata. This is the first of four posts walking through the same demo in C#, F#, XSLT, and\nplain JSON. The full project is on GitHub:\n[Kookerella.Demo.DecompileToSource](https://github.com/Kookerella-Ltd/Kookerella.Demo.DecompileToSource).\n\n## What we’re starting from\n\nA plain Excel file, built by hand in Excel. It has:\n\n- A bold, 16pt “INVOICE” title\n- A header row styled white-on-navy with a bottom border\n- Two sample line-item rows, with an\n`Amount`\n\ncolumn computed as`Qty * Unit Price`\n\nNobody wrote any code to produce this file — it’s just a spreadsheet.\n\n## Step 1: Install the tool\n\nThe reverse-engineering happens via a CLI tool, `fsopenxmldsl-mcp`\n\n, published as a\n`dotnet`\n\nglobal tool:\n\n```\ndotnet tool install -g Kookerella.FsOpenXmlDsl.Mcp\n```\n\n## Step 2: Reverse-engineer the template\n\n```\nfsopenxmldsl-mcp convert templates/InvoiceTemplate.xlsx --lang csharp -o decompiled/InvoiceTemplate.g.cs\n```\n\nThis reads the real `.xlsx`\n\nfile and prints out C# code that reproduces it exactly. The\ntop line of the result is:\n\n```\n#:package Kookerella.CsOpenXmlDsl@0.3.2\n```\n\nThat `#:package`\n\nline is a [.NET 10 file-based app](https://learn.microsoft.com/dotnet/core/whatsnew/dotnet-10#file-based-apps)\ndirective — this single `.cs`\n\nfile is a complete, runnable program with no `.csproj`\n\nneeded. You can run it immediately, on any machine with the .NET 10 SDK:\n\n```\ncd decompiled\ndotnet run InvoiceTemplate.g.cs\n```\n\nThat produces `output.xlsx`\n\n— pixel-for-pixel the same as the original template. Nothing\nwas lost in translation: title, header styling, borders, currency formatting, and the\n`Qty * Unit Price`\n\nformulas are all there, expressed as plain C# method calls\n(`Cell.Text(...)`\n\n, `.WithStyle(...)`\n\n, `Cell.Formula(...)`\n\n, and so on).\n\n**This is the point where “decompiling a spreadsheet” stops being a metaphor.** You now\nhave the template as source code you can read, diff, and edit.\n\n## Step 3: Adding the package to a real project\n\nThe file-based app above pinned the package inline for convenience. In a normal project you add it the usual way:\n\n```\ndotnet add package Kookerella.CsOpenXmlDsl\n```\n\n`Kookerella.CsOpenXmlDsl`\n\nis a C# wrapper around a separate F# core library,\n`Kookerella.FsOpenXmlDsl`\n\n, which does the actual OOXML reading/writing.\n\n## Step 4: Turning the decompiled snippet into real code\n\nThe decompiled file is disposable — it’s the starting point, not the deliverable. Nobody wants to hardcode “Widgets” and “Gadgets” forever. The diff from the decompiled file to a maintained version is deliberately small:\n\nWrap the top-level statements in a class and a\n\n`Build`\n\nmethod that takes real data (`IEnumerable<OrderLine>`\n\n) instead of running standalone.Replace the two hardcoded item rows with a LINQ projection over that data:\n\n``` js\nvar dataRows = orders.Select((order, i) =>\n{\n    var row = 4 + i;\n    return Row.Of(\n        Cell.Text(order.Item),\n        Cell.Number(order.Quantity),\n        Cell.Number(order.UnitPrice).WithStyle(CellStyle.Default.WithNumberFormat(NumberFormatKind.Currency)),\n        Cell.Formula($\"B{row}*C{row}\", order.Quantity * order.UnitPrice).WithStyle(CellStyle.Default.WithNumberFormat(NumberFormatKind.Currency)));\n});\n```\n\nSplice the generated rows into the sheet with a C# collection expression, instead of listing two hardcoded rows:\n\n``` js\nvar sheet0 = Sheet.Create(\n    \"Invoice\",\n    [\n        /* title row, exactly as decompiled */,\n        /* header row, exactly as decompiled */,\n        .. dataRows,\n    ]);\n```\n\nEverything else — every style call, every color, every border — is copied verbatim from the decompiled file. The styling was never hand-written by a developer in the first place, so there’s nothing to “port” — only the two data rows needed to become data-driven.\n\n## Step 5: Prove it stays correct\n\nBecause this is now real code, it can be tested like real code — something you can’t do\nto a `.xlsx`\n\nfile sitting on a file share. The test suite checks three things a\nspreadsheet can’t check about itself:\n\n**Schema validity**— runs a real`OpenXmlValidator`\n\nover the output. If a future change ever produces invalid OOXML, this catches it immediately instead of someone discovering a “repair this file?” dialog in Excel weeks later.**Correctness**— feeds in 0, 1, and 3 orders and checks the row count and the`Amount`\n\nformula text/value are correct for each one.**Styling preservation**— pins the exact colors, bold, font size, and border style that came from the original template, so a future edit can’t silently drift the invoice’s look away from what the original designer signed off on.\n\n## Recap\n\n| Step | Command | Output |\n|---|---|---|\n| Install the tool | `dotnet tool install -g Kookerella.FsOpenXmlDsl.Mcp` | `fsopenxmldsl-mcp` on your PATH |\n| Reverse-engineer | `fsopenxmldsl-mcp convert InvoiceTemplate.xlsx --lang csharp -o InvoiceTemplate.g.cs` | A runnable C# file that reproduces the original `.xlsx` exactly |\n| Add the package | `dotnet add package Kookerella.CsOpenXmlDsl` | The C# wrapper over the F# core |\n| Make it data-driven | Replace hardcoded rows with a LINQ projection over your own data model | A maintained generator |\n| Verify | `dotnet test` | Schema validity, formula correctness, and styling all pinned by real tests |\n\nThe template never had to be redesigned in code, and the developer never had to\nreverse-engineer OOXML XML by hand — the tool did that translation, once, and everything\nafter that is ordinary, testable C#. Next up: [the same demo in F#](/posts/decompiling-excel-template-2-fsharp/).", "url": "https://wpnews.pro/news/decompiling-an-excel-template-into-source-1-c", "canonical_source": "https://kookerella.com/posts/decompiling-excel-template-1-csharp/", "published_at": "2026-08-26 00:00:00+00:00", "updated_at": "2026-08-30 13:52:37.793440+00:00", "lang": "en", "topics": ["developer-tools"], "entities": ["Kookerella Ltd.", "fsopenxmldsl-mcp", "Kookerella.CsOpenXmlDsl", "Kookerella.FsOpenXmlDsl", ".NET 10"], "alternates": {"html": "https://wpnews.pro/news/decompiling-an-excel-template-into-source-1-c", "markdown": "https://wpnews.pro/news/decompiling-an-excel-template-into-source-1-c.md", "text": "https://wpnews.pro/news/decompiling-an-excel-template-into-source-1-c.txt", "jsonld": "https://wpnews.pro/news/decompiling-an-excel-template-into-source-1-c.jsonld"}}