xquery4 2.9.0
dotnet tool install --global xquery4 --version 2.9.0
dotnet new tool-manifest
dotnet tool install --local xquery4 --version 2.9.0
#tool dotnet:?package=xquery4&version=2.9.0
nuke :add-package xquery4 --version 2.9.0
xquery
Command-line XQuery 3.1/4.0 processor for .NET. Query XML documents from the terminal using the PhoenixmlDb XQuery engine.
Installation
dotnet tool install -g xquery4
Usage
# Query an XML file
xquery '//book/title' library.xml
# Count elements
xquery 'count(//item)' catalog.xml
# Read from a query file
xquery -f transform.xq input.xml
# Query a directory of XML files
xquery 'collection()//product[price > 50]' ./data/
# JSON output
xquery -o json 'map { "count": count(//item) }' data.xml
# Read from stdin
cat data.xml | xquery '//item/@name'
# Show execution plan
xquery --plan 'for $x in 1 to 10 return $x * $x'
# Show timing breakdown
xquery --timing '//item' large-catalog.xml
Features
- XQuery 3.1/4.0 — FLWOR, maps/arrays, higher-order functions, string constructors
- Multiple output methods — adaptive, XML, text, JSON
- Context item — input XML is available as
.(standard XQuery) - Multiple sources — files, directories, URLs, stdin
- Full prolog support — namespaces, variable/function declarations, serialization options
- Execution plans — inspect how queries are compiled and optimized
- Timing — built-in performance profiling
Documentation
Full documentation at phoenixml.dev
License
Apache-2.0
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | 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. |
This package has no dependencies.
| Version | Downloads | Last Updated |
|---|---|---|
| 2.9.0 | 0 | 10/15/2026 |
| 2.8.0 | 71 | 10/9/2026 |
| 2.7.0 | 66 | 10/7/2026 |
| 2.6.0 | 88 | 10/6/2026 |
| 2.5.1 | 89 | 10/1/2026 |
| 2.4.1 | 90 | 9/28/2026 |
| 2.4.0 | 71 | 9/28/2026 |
| 2.2.0 | 434 | 9/25/2026 |
| 2.1.0 | 79 | 9/17/2026 |
| 2.0.0 | 83 | 9/15/2026 |
| 1.8.0 | 89 | 9/14/2026 |
| 1.7.0 | 85 | 9/11/2026 |
| 1.6.15 | 84 | 9/9/2026 |
| 1.6.14 | 98 | 9/7/2026 |
| 1.6.13 | 85 | 9/4/2026 |
| 1.6.12 | 85 | 9/1/2026 |
| 1.6.11 | 91 | 8/29/2026 |
| 1.6.10 | 90 | 8/29/2026 |
| 1.6.9 | 88 | 8/27/2026 |
| 1.6.8 | 87 | 8/26/2026 |
Schema-aware typing continued, module base URIs, an implicit-timezone setting, and a faster
`group by`. QT3: 207 failing of 31,331 (2.8.0: 251). Requires PhoenixmlDb.Core 2.5.0.
### API
- **The implicit timezone is a setting of the query**: `QueryEngine.ImplicitTimezone`, or
`QueryExecutionContext.ImplicitTimezone` for one execution. It is the timezone a date, time
or dateTime with none of its own is taken to be in (comparison, arithmetic, `order by`,
`group by`, `distinct-values`, `min`, `max`, `adjust-*-to-timezone`), and the timezone of
`current-dateTime()`, `current-date()`, `current-time()` and `implicit-timezone()`. Unset,
it is the timezone of the machine as before, so nothing changes for a host that does not use
it; `TimeSpan.Zero` gives the same answers on every machine. Queries that run at the same
time each have their own. Needs the Core release that has `PhoenixmlDb.Xdm.ImplicitTimezone`.
- **`order by` compares dates, times and durations by value.** They were ordered as the text
of each value: `P10D` before `P9D`, and a dateTime with no timezone before the same one with
`Z` in every timezone.
- **`eq` on `xs:gYear`, `xs:gYearMonth`, `xs:gMonth`, `xs:gMonthDay` and `xs:gDay` uses the
implicit timezone** for a value with none. UTC was taken, whatever the implicit timezone.
- **A host can turn `fn:transform` off**: `ResourcePolicyBuilder.AllowTransformFunction(false)`,
read back as `ResourcePolicy.AllowTransformFunction`. It is on unless turned off, in every
preset and with no policy at all, so nothing changes for a host that does not use it. Turned
off, every way to call the function (by name, through a function item, `function-lookup`,
partial application, `fn:apply`) fails with FOXT0001 before the options are read, and
`function-lookup` does not find the function. A function of this kind implements
`IHostGatedFunction`.
- **A schema provider is told which module imports a schema**: `ISchemaProvider.ImportSchema` and
`AddSchemaText` have overloads that take the importing module's URI, and `XsdSchemaProvider`
passes it to the host's resolver as `ResourceRequest.ModuleUri` with each schema document it
asks for. Each overload defaults to the form without the module.
- **A schema provider can know the schema of the XML namespace**:
`new XsdSchemaProvider { KnowsXmlNamespace = true }`. A schema may import that namespace with
no `schemaLocation` (XSD 1.0 §4.2.6.2); System.Xml knows `xml:lang`, `xml:space` and
`xml:base` of itself and not `xml:id`, so a schema with `ref="xml:id"` did not compile. Such a
provider still takes a schema imported from files from `XsdSchemaProvider.ImportCache`. Off
by default.
- **The host's resolver is told which module asks** (#227): a document, a text or a module that
a query asks for comes to the resolver with `ResourceRequest.ModuleUri`, the module the
request is written in.
### Modules
- **A library module has its own static base URI** (#223). `static-base-uri()` and a relative
URI in a library module used the base URI of the main module.
- **An expression that a host language embeds can carry the location of its module** (#230):
`XQueryExpression.EmbeddedInModule`. The compiler of the host language sets it (an XPath
expression in an XSLT stylesheet), and the engine reports it to the resolver as
`ResourceRequest.ModuleUri`.
### Schema-aware queries
- **`fn:distinct-values` of validated nodes compares their typed values**: `1.0` and `1` of
type `xs:decimal` are one value, NOTATION values with different prefixes for one namespace
are one value, and each item of a list counts. The string values were compared.
- **`attribute(name)` with an unprefixed name matches an attribute in no namespace only**; an
attribute of that local name in any namespace matched.
- **An `xs:unsignedLong` above the range of `long` keeps its type**; it was a bare
`xs:integer`, so `xs:unsignedLong('18446744073709551615') instance of xs:unsignedLong` was false.
- **`XsdSchemaProvider.DeclaringEntities(names)`** (new): validates as if the document's DTD
declared those entities. A value of type `xs:ENTITY` names a declared entity, and the
validating parse reads no DTD, so every such value was "Reference to an undeclared entity".
- **The typed value of a node of a NOTATION type is an `xs:NOTATION`**, an instance of its own
type and not of `xs:QName`; no value was ever an instance of `xs:NOTATION`.
- **`fn:data`, `fn:sum`, `fn:avg`, `fn:min` and `fn:max` of nodes of a list type take each
item of their typed values**; each node gave the whole list as one value, so
`count(data($nodes))` counted nodes and `max` failed as "not orderable".
- **A validated element of a type with element-only content has no whitespace text
children** (XDM 3.1 §6.2.3); they were kept. Whitespace in mixed and simple content stays.
- **An element of a union type matches `element(*, U)` and `schema-element(N)`.** Since XPath
3.0 a node whose annotation is a member type of the union U matches `element(*, U)`; an
element of a union type is annotated with the member its value matched, and matched
neither. Unions within unions count too.
- **A validated attribute matches `attribute(*, T)` for a type it derives from**, as an element
does, and every atomic type matches `xs:anyAtomicType`. Only the untyped hierarchy was known
for attributes.
- **`schema-element(N)` with no prefix**, in a host language's expression, is in the host's
default namespace for elements (`xpath-default-namespace` in XSLT).
- **An element is validated without the constraints on a document as a whole.** `validate` of
an element, and `ISchemaProvider.ValidateXmlFragment`, refused an element that had the same
ID twice or an IDREF to no ID; those constraints are for a document (XQuery 3.1 §3.21.2).
New: `ISchemaProvider.ValidateElementAndAnnotate`.
- **A date with a negative year, or a year of more than four digits, is valid.** System.Xml
reads a date into a DateTime, which has neither, and reported `-0055-10-10` as an invalid
`xs:date`; a document with such a date in an `xs:date`, `xs:dateTime`, `xs:gYear` or
`xs:gYearMonth` did not validate. XML Schema allows them.
- **`validate type T { … }` validates against the type.** The type was not used: the element
was validated by its declaration, and an element with no declaration passed with any content
and stayed untyped. The element is now validated against `T` whatever is declared for its
name, and the result has the type annotations of `T` and of its content. The same holds for
`ISchemaProvider.ValidateXml`, `ValidateXmlFragment` and `ValidateAndAnnotate` with a type
name, which XSLT uses for its `type` attribute. QT3: 213 failing (was 222).
- **The same in a kind test**: `element(*, moneyType)` and `attribute(*, T)` with a type name
that has no prefix, in an expression of a host language, name a type in the default
namespace for elements and types. It was XPST0008 "unknown schema type".
- **Each element of a fragment is validated against the type** by
`ISchemaProvider.ValidateXmlFragment` with a type name; more than one element was refused.
- **A type name with no prefix, in an expression of a host language, can name a type of an
imported schema**: it is a type in the default namespace for elements and types, which the
host gives (in XSLT, `xpath-default-namespace`, or no namespace). `'7' castable as small`
was XPST0051 for a type `small` of a schema with no target namespace.
- **A kind test in an expression of a host language takes its prefixes from the host**:
`schema-element(o:order)`, `schema-attribute(o:a)`, `element(*, o:orderType)` and
`attribute(*, o:t)` with a prefix that the stylesheet binds. The declaration was looked for
in no namespace, so `instance of schema-element(o:order)` was false for every element in
XSLT, and a prefixed type name in `element(*, T)` was XPST0081.
- **A pattern facet of an imported schema counts characters by code point** (#231): `.` and a
character class match one character outside the Basic Multilingual Plane, where they matched
half of one. Needs the Core release that follows 2.4.0.
- **The length facets of a string type of an imported schema count characters** (#238):
`length`, `minLength` and `maxLength` counted UTF-16 units, so `maxLength="1"` refused one
character outside the Basic Multilingual Plane. The message of a value that is refused is the
same as before. Needs the Core release that follows 2.4.0.
- **A nilled element atomizes to the empty sequence** (#236). An element validated with
`xsi:nil="true"` gave the value of its type for the empty string, or an error where the
empty string is not a value of the type.
- **A schema type is carried through in three more places** (#237): an item of a list of
unions has the value of the first member type its text is valid for, where it stayed
`xs:untypedAtomic`; a union with a built-in list type as a member (`xs:IDREFS`,
`xs:NMTOKENS`, `xs:ENTITIES`) casts to it; and a variable or a context item declared as
`schema-element(N)` accepts a validated element of that declaration.
- **`schema-element()`, `schema-attribute()` and `namespace-node()` are node types** (#233).
A parameter declared as `schema-element(N)` was taken for an atomic type and its argument
was atomized (FOTY0012 for an element with element-only content). The same holds for a
declared return type, a typed `for` binding and a typed grouping key. A declared
`schema-element(N)` return type is now matched against the schema: the name, its
substitution group and the type annotation.
### Functions and errors
- **`file:/abs/path` is a file URI** (#229): `doc`, `doc-available` and `json-doc` threw
`UriFormatException` for it.
- **The error of the specification where the runtime's own exception came out** (#241). A
function of the wrong arity given to `for-each`, `filter`, `array:for-each`, `array:filter`,
`fold-left` or `fold-right` is XPTY0004. `array:head` and `array:tail` of an empty array, and
an array called with an index too large for an integer, are FOAY0001. An arity too large in
a named function reference is FOAR0002. A function item as an argument of `concat` is
FOTY0013.
### Performance
- **`group by` finds the group of a tuple by the hash of its key** (#232). Each tuple was
compared with every group so far, so a grouping into many groups took time in proportion to
the square of their number. It also obeys cancellation while it groups.
- **A child step from nodes that are in document order does not sort its result, and a
predicate that is one comparison or one function call is evaluated without an iterator**
(#239). Results are the same.
### `xquery` CLI (ships on `cli-v2.9.0`)
- Runs on **PhoenixmlDb.Xslt 2.9.0**, so `fn:transform` from a query gets the 2.9.0 engine.