PhoenixmlDb.Xslt 1.6.8

dotnet add package PhoenixmlDb.Xslt --version 1.6.8
                    
NuGet\Install-Package PhoenixmlDb.Xslt -Version 1.6.8
                    
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="PhoenixmlDb.Xslt" Version="1.6.8" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="PhoenixmlDb.Xslt" Version="1.6.8" />
                    
Directory.Packages.props
<PackageReference Include="PhoenixmlDb.Xslt" />
                    
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 PhoenixmlDb.Xslt --version 1.6.8
                    
#r "nuget: PhoenixmlDb.Xslt, 1.6.8"
                    
#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 PhoenixmlDb.Xslt@1.6.8
                    
#: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=PhoenixmlDb.Xslt&version=1.6.8
                    
Install as a Cake Addin
#tool nuget:?package=PhoenixmlDb.Xslt&version=1.6.8
                    
Install as a Cake Tool

PhoenixmlDb.Xslt

XSLT 4.0 transformation engine for PhoenixmlDb — transform XML documents into HTML, JSON, CSV, text, or other XML formats.

Features

  • XSLT 3.0/4.0 — template matching, streaming, accumulators, packages, maps/arrays
  • All output methods — HTML5, XML, XHTML, JSON, text, CSV, adaptive
  • Multiple outputsxsl:result-document for multi-file generation
  • Full XPath 4.0 — 240+ built-in functions available in all expressions
  • Packages — reusable stylesheet libraries with visibility control

Quick example

using PhoenixmlDb.Xslt;

var transformer = new XsltTransformer();
await transformer.LoadStylesheetAsync(
    File.ReadAllText("style.xsl"),
    new Uri(Path.GetFullPath("style.xsl")));

transformer.SetParameter("title", "My Report");
var html = await transformer.TransformAsync(
    File.ReadAllText("data.xml"));

// Handle secondary outputs (xsl:result-document)
foreach (var (href, content) in transformer.SecondaryResultDocuments)
    File.WriteAllText(Path.Combine(outputDir, href), content);

See the full API overview in the README.

Package Description
PhoenixmlDb.Core Core types and XDM data model (dependency)
PhoenixmlDb.XQuery XQuery 4.0 query engine (dependency)
PhoenixmlDb.Xslt.Cli xslt command-line tool

Documentation

Full documentation at phoenixml.dev

License

Apache 2.0

Product Compatible and additional computed target framework versions.
.NET net8.0 is compatible.  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. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
1.6.8 30 8/26/2026
1.6.7 54 8/24/2026
1.6.6 61 8/23/2026
1.6.5 82 8/22/2026
1.6.4 117 8/16/2026
1.6.3 88 8/14/2026
1.6.2 86 8/13/2026
1.6.1 106 8/4/2026
1.6.0 108 7/31/2026
1.5.0 127 7/27/2026
1.4.25 106 7/17/2026
1.4.24 88 7/14/2026
1.4.23 131 7/12/2026
1.4.22 107 7/10/2026
1.4.21 111 7/9/2026
1.4.20 103 7/9/2026
1.4.19 100 7/9/2026
1.4.18 101 7/8/2026
1.4.17 104 7/7/2026
1.4.16 130 6/30/2026
Loading failed

Four engine fixes, two of them from Martin Honnen's XSpec testing, and a conformance harness
that turned out to be measuring nothing at all. XSpec suites completing end to end went from
24 to 60.

### A typed template's `xsl:copy` of an attribute went to the caller's element

Reported by Martin Honnen against XSpec 4.0.3, which failed with
`XTTE0505: Template 'identity' return value does not match declared type Node: expected
exactly one item, got 0`.

The attribute was never lost — it was delivered to the wrong element. `xsl:copy` of an
attribute joined a typed template's result sequence only when nothing else was collecting
attributes, but XSpec's identity template is invoked while the CALLER's `xsl:copy` is still
collecting its own. So `@status` was appended to the caller's element and the template
returned nothing, and the error blamed the template rather than naming where the item went.

Found while fixing it: `<xsl:copy/>` with no content on a document node produced nothing at
all, because both code paths required content. A shallow copy with no content is legal.

### One implementation of global content binding, not two

The engine had two implementations of "bind a global variable from a content sequence
constructor". The eager, dependency-ordered pass honoured the declared `as` type; the lazy
on-demand pass stored the serialized content as a raw `xs:string` and ignored `as` entirely.
So one declaration bound a document node or a string depending on which pass reached it
first — and which pass ran depended on whether the dependency analysis spotted the reference.
It does not recognise EQName (`$Q{uri}local`) references, which XSpec generates throughout.

The eager path already carried a fix for this exact `XPTY0020`, from Martin's earlier DocBook
report. It had been applied to one of the two paths.

### A typed variable with empty content is `()`, not `""`

XSLT 3.0 §9.3 gives the zero-length-string rule for a variable with "neither a select
attribute nor an as attribute". The `as` clause was missing from the implementation, so every
typed empty binding held a string: `<xsl:variable as="empty-sequence()"/>` reported a count of
1, and `<xsl:variable as="element(x)*"/>` held a string rather than elements.

### Accumulators: untypedAtomic is converted to the declared type

`CoerceAtomicValue` had no arm for `XsUntypedAtomic` — what atomizing a node actually yields —
so an accumulator declared `as="xs:double"` whose rule selected `@amount` silently kept its
seed and reported it as the answer. The sibling `sum` accumulator was correct throughout,
because arithmetic operators atomize; one stylesheet gave a right sum and a wrong min.

Accumulator errors are deferred per spec, but "deferred" had been implemented as
`catch (Exception)` at five sites with three different behaviours — swallowing cancellation
and CLR faults, which is how the type bug stayed invisible. Now only genuine dynamic errors
defer, and rethrow preserves the original stack.

### `XsltException` carries an `ErrorCode`

The engine asked "which error is this?" by searching message text in seven places, five with
`Contains` — so an error merely mentioning `XTDE0640` in its prose matched as that error.

### The conformance suite was measuring nothing

Not a shipped-engine bug, but worth recording: the XSLT conformance run reported
"257 passed" having executed zero cases. The fixture checked `Directory.Exists` against the
empty directory skeleton the build creates, so every test set loaded no cases and each
reported "all filtered by dependencies" — a conclusion the code never verified. Fixed, along
with the harness refusing DTDs the corpus ships and declaring streaming unsupported when it
is 95.9% conformant.

   Xslt.Tests 1289 passed, 0 failed, 1 skipped (1204 at 1.6.7)
   XSLT conformance 10232/10630 = 96.26% — the first figure measured against a corpus
     that was actually loaded
   XSpec: 60 of 284 suites complete end to end, up from 24