A standalone Blazor WebAssembly application that compares two Crystal Reports definitions (exported to XML) and highlights only the differences that actually affect how a report renders — field layout, section dimensions, text content, data sources, suppression conditions, formulas and print options.
Everything runs entirely in the browser. No file is ever uploaded to a server, which makes the tool safe to use with sensitive report definitions.
This project is a decoupled, self-contained reimplementation of an internal Cirrus demo tool. The comparison logic was extracted into a reusable, framework-agnostic library and paired with a brand-new UI so it can run on its own without the surrounding platform.
Crystal Reports .rpt files are opaque binaries, so reviewing what changed
between two versions of a report is painful. Standard text diff tools are
useless on the exported XML because they drown you in noise: reordered nodes,
regenerated identifiers, connection strings and other metadata that have no
visual impact on the printed report.
This tool solves that by performing a semantic, rendering-focused diff:
- It understands the report structure (areas, sections, objects, formulas).
- It compares only the properties that change the output.
- It ignores ordering, metadata and database connection details.
- It produces both a visual side-by-side layout and a categorized, filterable change list.
Typical uses:
- Reviewing a report change before shipping it.
- Auditing what a colleague modified in a report.
- Troubleshooting "the report looks different" reports.
.rpt files (Crystal Reports 11) are converted to XML with the open-source
RptToXml tool. The app accepts .xml
files (or .txt files containing XML).
Upload the original (old) report on the left and the modified (new) report on the right.
Clicking Compare runs two independent passes over the documents:
XmlDiffEngineproduces the textual, categorized list of differences.ReportLayoutParserbuilds a geometric model of each report and annotates every element as unchanged / added / removed / modified so the two layouts can be drawn side by side.
- A visual preview renders both reports as scaled pages, colour-coding each element by its change type.
- A detailed change summary groups differences by section and category and can be filtered by change type, category and section.
The solution is split into three projects to keep the domain logic fully decoupled from the UI and independently testable.
Crystal-Reports-Diff.sln
├── CrystalReportsDiff.Core # Pure, framework-agnostic domain library
│ ├── Models/ # ReportLayout, XmlDiffEntry, DiffSummary, ...
│ ├── Comparison/ # ICrystalReportComparer + XmlDiffEngine
│ │ └── Rules/ # One IReportComparisonRule per concern
│ ├── Layout/ # IReportLayoutParser + ReportLayoutParser
│ └── Internal/ # Shared XML helpers
├── Crystal-Reports-Diff # Blazor WebAssembly app (the UI)
│ ├── Pages/ # CrystalReportsDiffPage
│ ├── Components/ # ReportLayoutView (visual preview)
│ └── Features/CrystalReportsDiff/ # CrystalReportsDiffState (presentation model)
└── CrystalReportsDiff.Core.Tests # xUnit tests for the Core library
- Separation of concerns — all comparison/parsing logic lives in
CrystalReportsDiff.Coreand has no dependency on Blazor. The web app is a thin presentation layer over it. The library could just as easily power a CLI, a desktop app or a server API. - Strategy pattern —
XmlDiffEngineowns no comparison logic itself. It runs an ordered set ofIReportComparisonRulestrategies (PrintOptionsComparisonRule,AreasComparisonRule,FormulaFieldsComparisonRule,RunningTotalsComparisonRule). Each rule is independent, single-responsibility and individually testable, and callers can add, remove or reorder rules. - Programming to interfaces + dependency injection — the UI depends on
ICrystalReportComparerandIReportLayoutParser, registered inProgram.cs, not on concrete types. - Presentation model —
CrystalReportsDiffStateholds all UI state and orchestrates the services, keeping the Razor markup declarative and the state logic unit-testable. - Clean code — small focused methods, meaningful names, no dead code, XML documentation on the public surface.
Requirements: .NET 9 SDK.
cd Crystal-Reports-Diff
dotnet run --project Crystal-Reports-DiffThen open the printed URL (for example http://localhost:5219).
cd Crystal-Reports-Diff
dotnet testThe CrystalReportsDiff.Core.Tests project covers the comparison engine (every
change category, summary consistency, rule composition, invalid input) and the
layout parser (parsing, defaults, change annotation, twip scaling).
| Compared (affects rendering) | Ignored (no visual impact) |
|---|---|
| Object position and size | Node ordering |
| Section height | Database connection details |
| Text content | Save/preview metadata flags |
| Data source expressions | Class factory names |
| Suppression / visibility conditions | Parameter/sort/group internal state |
| Page size, orientation, margins | Report options metadata |
| Formula field expressions | |
| Running total operations |
- .NET 9 / C#
- Blazor WebAssembly (standalone)
- Bootstrap 5 + Bootstrap Icons (via CDN) for styling
- xUnit for testing