XmlAuditfile 0.0.7

dotnet add package XmlAuditfile --version 0.0.7
                    
NuGet\Install-Package XmlAuditfile -Version 0.0.7
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="XmlAuditfile" Version="0.0.7" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="XmlAuditfile" Version="0.0.7" />
                    
Directory.Packages.props
<PackageReference Include="XmlAuditfile" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add XmlAuditfile --version 0.0.7
                    
#r "nuget: XmlAuditfile, 0.0.7"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package XmlAuditfile@0.0.7
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=XmlAuditfile&version=0.0.7
                    
Install as a Cake Addin
#tool nuget:?package=XmlAuditfile&version=0.0.7
                    
Install as a Cake Tool

auditfile-dotnet

Streaming reader for Dutch tax audit files (XAF and related), following auditfile-spec.

Status: draft (0.x). Not affiliated with the Belastingdienst or any software vendor.

Packages

Package Contents
XmlAuditfile The reader: version resolution, validation, ISink, row streaming. No dependencies.
XmlAuditfile.Memory MemorySink: keeps all rows per table in memory. Depends on the reader.

Write your own ISink to load into a database or anything else; only the reader package is needed then.

using XmlAuditfile;
using XmlAuditfile.Memory;

var sink = new MemorySink();
var source = AuditfileSource.FromFactory(() => File.OpenRead("administratie.xaf"), "administratie.xaf");
var report = await Auditfile.ReadAsync(source, sink);

foreach (var diagnostic in report.Diagnostics)
{
    Console.WriteLine(diagnostic);
}

foreach (var line in sink.Rows("tr_line"))
{
    Console.WriteLine($"{line.GetString("acc_id")} {line.GetDecimal("amnt")} {line.GetString("amnt_tp")}");
}

Rows have typed accessors (GetString, GetDecimal, GetInteger, GetDate, GetDateTime, GetTime, GetBoolean) that check the column type and name the problem; TryGetValue tells whether a column exists in this version. GetDecimal also reads an integer column, since a column can be integer in one version and decimal in another. In a loop over many rows, look the column up once (table.FindColumn("amnt")) and pass the SchemaColumn: no lookup by name per row.

Values are read in XSD notation only: a date-time such as 03/01/2017 10:00:00 is refused (XAF-S004) rather than guessed.

What it does

  • Determines the version: forced in code, official namespace or alias from the registry, optionally derived from the namespace (UseResolver). Anything else stops with a clear diagnostic.
  • Reads in two phases: scan (validate against the built-in XSD, count rows per table) and load (stream rows in batches to a sink). Verified mode loads only a valid file.
  • Turns every repeating element into a table following the manifest generated by auditfile-spec. Rows know their id, parent, position and source part; values are typed (decimal, long, DateTime, ...).
  • Checks required and expected tables, keys, references and the XAF business rules (balanced transactions, header counts and totals).
  • Reads split audit files (audit.xaf, audit-2.xaf, ...) as one file; you pass the parts in order.
  • Reports everything as diagnostics with stable codes, in Dutch or English.

Entry points

Call Use
Auditfile.ReadAsync(source, sink, options) Read into a sink: MemorySink (Memory package) or your own ISink
Auditfile.ValidateAsync(source, options) Validate without storing anything; Mode = Unverified skips the XSD
Auditfile.Scan(source, options) Version, XSD validation and row counts only
Auditfile.Open(source, options).ReadRows() Stream rows yourself; the rows can be enumerated once

Every call takes a CancellationToken, checked in the scan phase as well as the load phase. ReadOptions refuses values out of range (BatchSize, MaxDiagnostics, MaxDepth, MaxValueLength at least 1) with an ArgumentOutOfRangeException. When a sink fails and its AbortAsync fails too, the read throws an AggregateException with both errors. | AuditfileVersion.Financial.V3_2 | Manifest, XSD, definitions and dialects of a version | | Auditfile.Dialects.Add(version, json) / Auditfile.Views.Add(json) | Add your own dialects and views next to the built-in ones (see below) | | AuditfileSchema.ToJson() / FromJson() | Store and restore the schema of an import |

Sources: the reader never opens files; you supply the streams.

Call Use
AuditfileSource.FromFactory(open, name) Fresh stream per call: can be read twice (scan, then load)
AuditfileSource.FromFactories((name, open), ...) Same, for the parts of a split audit file
AuditfileSource.FromStream(stream, name) Read once; validation then happens during loading
AuditfileSource.FromStreams((name, stream), ...) Same, for the parts of a split audit file
// Files on disk, e.g. audit.xaf and audit-2.xaf
var source = AuditfileSource.FromFactories(paths.Select(p => (Path.GetFileName(p), (Func<Stream>)(() => File.OpenRead(p)))).ToArray());

Custom dialects and views

Dialects and views come from auditfile-spec and are embedded in the package. You can add your own at runtime, in the same format as dialects.json and views/<type>.json of the spec, without a fork or a custom build. Pass the JSON text or a UTF-8 stream; the reader opens no files, so open them yourself.

// { "formatVersion": 1, "dialects": [ { "name": "my-package", "match": { ... }, "corrections": [ ... ] } ] }
Auditfile.Dialects.Add(AuditfileVersion.Financial.V3_2, File.ReadAllText("dialects.json"));

// { "formatVersion": 1, "type": "xaf", "views": [ { "name": "lines_per_account", ... } ] }
using (var views = File.OpenRead("views.json"))
{
    Auditfile.Views.Add(views);
}

var report = await Auditfile.ReadAsync(source, sink);      // recognizes and applies the added dialect
var result = sink.Run(sink.Schema!.Version!.FindView("lines_per_account")!);
  • Added definitions come after the built-in ones in AuditfileVersion.Dialects and Views, so FindView, version resolution and dialect corrections use them like the built-in ones. A view is added for every version of its type; whether it fits a version is decided when it runs (XAF-Q001), as for built-in views. ApplyDialects = false ignores added dialects too.
  • A file is checked when it is added and refused as a whole when anything is wrong (FormatException): invalid JSON, a formatVersion other than 1, an unknown dialect action, view aggregate or operator, or a correction code that is not a dialect diagnostic (XAF-C001–C003). An unknown audit file type or a name the version or type already has throws an ArgumentException. Built-in definitions cannot be replaced or removed.
  • Auditfile.Dialects.Remove(version, name) and Auditfile.Views.Remove(type, name) remove one added definition; Clear() removes all of them. The registries are thread-safe and shared by every read in the process; a read that has started keeps the dialects it found.

Targets

netstandard2.0 (.NET Framework 4.7.2+) and net10.0. No package dependencies on any target. On net10.0 the library is trimming and Native AOT compatible; the trim and AOT analyzers run in every build. Example programs live in auditfile-dotnet-samples.

WebAssembly: the library uses no file system, threads or reflection; all input comes from your streams. Browser file streams are asynchronous only, so copy the file into memory first and use AuditfileSource.FromFactory(() => new MemoryStream(bytes)).

Web applications: the XML is parsed synchronously; only the sink is called asynchronously. Parsing is CPU-bound, and asynchronous reads per node would only slow it down. ASP.NET Core does not allow synchronous reads of the request body, so do not pass Request.Body to FromStream: copy the upload to a temporary file and read that. A large file keeps a thread busy while it is read, so run big imports in a background job rather than in the request.

var path = Path.GetTempFileName();
try
{
    await using (var file = File.Create(path))
    {
        await upload.CopyToAsync(file, cancellationToken); // upload: IFormFile
    }

    var report = await Auditfile.ValidateAsync(
        AuditfileSource.FromFactory(() => File.OpenRead(path), upload.FileName), cancellationToken: cancellationToken);
}
finally
{
    File.Delete(path);
}

Diagnostics

Every finding is a diagnostic with a stable code from diagnostics.json of auditfile-spec. The report lists at most MaxDiagnostics (default 1000), followed by XAF-X900 when more were found. A full list makes room for errors by leaving out infos and warnings, and fatal diagnostics are always included. IsValid, HasErrors, HasFatal and Count(severity) count every diagnostic, also the ones left out; IsTruncated says whether some were.

Layer Codes this reader reports
XML X001–X004 (X004 also for a part of a split file in another encoding than the first part), X900
Version V001–V006
Structure S001–S005, S008; S006 in unverified mode (in verified mode the XSD reports a missing field as D001)
XSD D001, D002
Rules R001–R004, R017
Dialect C001–C003, when a dialect applies
Views Q001, Q002

Defined in the spec but not checked yet: S007 and S009 (in verified mode the XSD reports too many occurrences and values outside a code list as D001), R005–R016 (ascending numbers, periods, eleven test, IBAN, personal data), X005 (the XML parser reports an invalid character as X001) and E001–E004 (envelopes).

Layout

Path Contents
src/XmlAuditfile/ The reader
src/XmlAuditfile.Memory/ MemorySink
tests/XmlAuditfile.Tests/ Unit tests, using the XAF 3.2 sample in Data/xaf-3.2-sample.xaf (two intended findings: XAF-R001, XAF-R003)
spec.lock.json The pinned auditfile-spec release
tools/ fetch-spec.ps1 and the semver helpers used by the workflows

Definitions

Everything in generated/ of a pinned auditfile-spec release (XSD, manifests, definitions, dialects, registry, diagnostics) is embedded in the package. The version objects (AuditfileVersion) are part of this reader; a test checks that they match the registry.

The spec is not committed. spec.lock.json pins a release (tag and commit) of auditfile-spec, and tools/fetch-spec.ps1 copies only generated/ of that commit into spec/generated/ (git-ignored; a plain folder, not a git checkout). The build stops with a clear error when spec/ is missing.

pwsh tools/fetch-spec.ps1        # after cloning and whenever spec.lock.json changes
dotnet build XmlAuditfile.slnx
dotnet test --solution XmlAuditfile.slnx

To build against a local spec checkout instead: dotnet build -p:SpecRoot=../auditfile-spec/.

Workflows

Workflow What it does
ci.yml Push to main and pull requests: fetch the pinned spec, build, test
release.yml Manual, with a version such as 1.0.1. From main the version must be higher than every release; from release/X.Y (maintenance of an older line, e.g. a security fix 0.1.1 after 1.0.0, branched off tag 0.1.0) it must be in line X.Y and higher than the latest release in that line. Then it fetches the pinned spec, builds, tests, packs, creates the tag and GitHub release, and publishes the packages and symbols to NuGet through Trusted Publishing when the NUGET_USER variable is set (environment nuget)
spec-update.yml Weekly: when auditfile-spec has a higher release than spec.lock.json, updates the lock, builds and tests, and opens a pull request with the result
dependabot.yml Weekly updates of NuGet packages and GitHub Actions

License

Apache License 2.0.

Product Compatible and additional computed target framework versions.
.NET net5.0 was computed.  net5.0-windows was computed.  net6.0 was computed.  net6.0-android was computed.  net6.0-ios was computed.  net6.0-maccatalyst was computed.  net6.0-macos was computed.  net6.0-tvos was computed.  net6.0-windows was computed.  net7.0 was computed.  net7.0-android was computed.  net7.0-ios was computed.  net7.0-maccatalyst was computed.  net7.0-macos was computed.  net7.0-tvos was computed.  net7.0-windows was computed.  net8.0 was computed.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed.  net9.0 was computed.  net9.0-android was computed.  net9.0-browser was computed.  net9.0-ios was computed.  net9.0-maccatalyst was computed.  net9.0-macos was computed.  net9.0-tvos was computed.  net9.0-windows was computed.  net10.0 is compatible.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
.NET Core netcoreapp2.0 was computed.  netcoreapp2.1 was computed.  netcoreapp2.2 was computed.  netcoreapp3.0 was computed.  netcoreapp3.1 was computed. 
.NET Standard netstandard2.0 is compatible.  netstandard2.1 was computed. 
.NET Framework net461 was computed.  net462 was computed.  net463 was computed.  net47 was computed.  net471 was computed.  net472 was computed.  net48 was computed.  net481 was computed. 
MonoAndroid monoandroid was computed. 
MonoMac monomac was computed. 
MonoTouch monotouch was computed. 
Tizen tizen40 was computed.  tizen60 was computed. 
Xamarin.iOS xamarinios was computed. 
Xamarin.Mac xamarinmac was computed. 
Xamarin.TVOS xamarintvos was computed. 
Xamarin.WatchOS xamarinwatchos was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.
  • .NETStandard 2.0

    • No dependencies.
  • net10.0

    • No dependencies.

NuGet packages (1)

Showing the top 1 NuGet packages that depend on XmlAuditfile:

Package Downloads
XmlAuditfile.Memory

In-memory sink for XmlAuditfile: keeps all rows per table. Useful for tests, small files and the browser.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
0.0.7 0 10/29/2026
0.0.6 60 10/28/2026
0.0.5 91 10/27/2026
0.0.4 106 10/25/2026
0.0.3 99 10/25/2026
0.0.2-alpha 101 10/24/2026