View Old View New View Both View Only Previous Next

This draft contains only sections that have differences from the version that it modified.

W3C

XSL Transformations (XSLT) Version 4.0

W3C Editor's Draft 23 February 2026

This version:
https://qt4cg.org/specifications/xslt-40/
Latest version:
https://qt4cg.org/specifications/xslt-40/
Most recent Recommendation of XSL Transformations (XSLT):
https://www.w3.org/TR/xslt-30/
Editor:
Michael Kay, Saxonica <http://www.saxonica.com/>

The following associated resources are available: Specification in XML format, XSD 1.1 Schema for XSLT 4.0 Stylesheets (non-normative), Relax-NG Schema for XSLT 4.0 Stylesheets (non-normative), Stylesheet for XML-to-JSON conversion (non-normative)


Abstract

This specification defines the syntax and semantics of XSLT 4.0, a language designed primarily for transforming XML documents into other XML documents.

XSLT 4.0 is a revised version of the XSLT 3.0 Recommendation [XSLT 3.0] published on 8 June 2017. Changes are presented in 1.2 What’s New in XSLT 4.0?.

XSLT 4.0 is designed to be used in conjunction with XPath 4.0, which is defined in [XPath 4.0]. XSLT shares the same data model as XPath 4.0, which is defined in [XDM 3.0], and it uses the library of functions and operators defined in [Functions and Operators 4.0]. XPath 4.0 and the underlying function library introduce a number of enhancements, for example the availability of union and record types.

This document contains hyperlinks to specific sections or definitions within other documents in this family of specifications. These links are indicated visually by a superscript identifying the target specification: for example XP for XPath 4.0, DM for the XDM data model version 4.0, FO for Functions and Operators version 4.0.

Status of this Document

This section describes the status of this document at the time of its publication. Other documents may supersede this document. A list of current W3C publications and the latest revision of this technical report can be found in the W3C technical reports index at https://www.w3.org/TR/.

This document has no official standing. It is produced by the editor as a proposal for community review. Insofar as it copies large amounts of text from the W3C XSLT 3.0 Recommendation, W3C copyright and similar provisions apply.

Dedication

The publications of this community group are dedicated to our co-chair, Michael Sperberg-McQueen (1954–2024).


5 Features of the XSLT Language

5.5 Named Types

XSLT 4.0 introduces two new and closely-related constructs allowing item types to be given names, and to be referred to by name anywhere that item types are used, for example in function declarations and variable declarations.

The xsl:item-type declaration allows any item type to be given a name. It is particularly useful to avoid repetitive use of the same choice types, enumeration types, or function types, and means that if the definition changes, the change only needs to be made in one place.

The xsl:record-type declaration takes this a step further. Like xsl:item-type, it allows a name to be given to any record type. In addition, though, it offers two further capabilities:

  • Named record types can be recursive, allowing definitions of recursive data structures such as lists and trees.

  • Declaring a named record type automatically establishes a constructor function for records of that type; the constructor function has the same name as the record type itself.

5.5.3 Example: Defining an Atomic Set

This example demonstrates a library package that provides a new data type to manipulate sets of atomic items.

A stylesheet that incorporates this package (by referencing it in an xsl:use-package declaration) can use constructs such as:

  • <xsl:variable name="empty" 
           as="set:atomic-set"
           select="set:build(())"/>

  • <xsl:variable name="evens"
          as="set:atomic-set"                        
          select="set:build((1 to 100)[. mod 2 = 0])"/>

  • <xsl:variable name="odds"
          as="set:atomic-set"
          select="set:build((1 to 100)) ? except($evens)"/>

  • <xsl:function name="my:is-even" as="xs:boolean">
       <xsl:param name="n" as="xs:integer"/>
       <xsl:sequence select="$evens ? contains($n)"/>
    </xsl:function>

Here is the implementation of the package. It relies heavily on the use of the function annotation %method, which gives a function item contained in a map access to the containing map: methods are described in Section 4.5.6.1 MethodsXP.

<xsl:package name="http://qt4cg.org/atomic-set"
             package-version="1.0.0"
             xmlns:set="http://qt4cg.org/atomic-set"
             xmlns:map="http://www.w3.org/2005/xpath-functions/map"
             xmlns:xs="http://www.w3.org/2001/XMLSchema">
   
   <xsl:note>
      This package defines a type set:atomic-set which represents
      a set of distinct atomic items. Atomic items are considered
      distinct based on the comparison function fn:atomic-equal.
      
      An instance of an atomic set can be constructed using a function
      call such as set:build((1, 3, 5, 7, 9)).
      
      If $A and $B are instances of set:atomic-set, then they
      can be manipulated using methods including:
      
      $A?size() - returns the number of items in the set
      $A?empty() - returns true if the set is empty
      $A?contains($k) - determines whether $k is a member of the set
      $A?contains-all($B) - returns true if $B is a subset of $A
      $A?values() - returns the items in $A, as a sequence
      $A?add($k) - returns a new atomic set containing an additional item
      $A?remove($k) - returns a new atomic set in which the given item is absent
      $A?union($B) - returns a new atomic set containing the union of $A and $B
      $A?intersect($B) - returns a new atomic set containing the intersection of $A and $B
      $A?except($B) - returns a new atomic set containing the difference of $A and $B
   
      This package defines a type set:atomic-set which represents
      a set of distinct atomic items. Atomic items are considered
      distinct based on the comparison function fn:atomic-equal.
      
      An instance of an atomic set can be constructed using a function
      call such as set:build((1, 3, 5, 7, 9)).
      
      If $A and $B are instances of set:atomic-set, then they
      can be manipulated using methods including:
      
      $A?size() - returns the number of items in the set
      $A?empty() - returns true if the set is empty
      $A?contains($k) - determines whether $k is a member of the set
      $A?contains-all($B) - returns true if $B is a subset of $A
      $A?values() - returns the items in $A, as a sequence
      $A?add($k) - returns a new atomic set containing an additional item
      $A?remove($k) - returns a new atomic set in which the given item is absent
      $A?union($B) - returns a new atomic set holding the union of $A and $B
      $A?intersect($B) - returns a new atomic set holding the intersection of $A and $B
      $A?except($B) - returns a new atomic set holding the difference of $A and $B
   </xsl:note>
   
   <xsl:record-type name="set:atomic-set" 
                    visibility="public"
                    extensible="yes">
      <xsl:field name="_data" 
                 as="map(xs:anyAtomicType, xs:boolean)"/>
      <xsl:field name="size" 
                 as="%method fn() as xs:integer"/>
      <xsl:field name="empty" 
                 as="%method fn() as xs:boolean"/>
      <xsl:field name="contains" 
                 as="%method fn($value as xs:anyAtomicType) as xs:boolean"/> 
      <xsl:field name="contains-all" 
                 as="%method fn($value as set:atomic-set) as xs:boolean"/> 
      <xsl:field name="add" 
                 as="%method fn($item as xs:anyAtomicType) as set:atomic-set"/> 
      <xsl:field name="remove" 
                 as="%method fn($item as xs:anyAtomicType) as set:atomic-set"/>
      <xsl:field name="union" 
                 as="%method fn($value as set:atomic-set) as set:atomic-set"/>
      <xsl:field name="intersect" 
                 as="%method fn($value as set:atomic-set) as set:atomic-set"/> 
      <xsl:field name="except" 
                 as="%method fn($value as set:atomic-set) as set:atomic-set"/> 
   </xsl:record-type>
   
   <xsl:variable name="DATA" select="'_data'" visibility="private"/>
   
   <xsl:note>
      The private function set:replaceData processes the internal map
      by applying a supplied function, and returns a new atomic set
      with the resulting internal map 
   </xsl:note>
   
   <xsl:function name="set:replaceData" 
                 visibility="private" 
                 as="map(xs:anyAtomicType, xs:boolean)">
      <xsl:param name="input" as="set:atomic-set"/>
      <xsl:param name="update" as="fn(map(*)) as map(*)"/>
      <xsl:sequence select="map:put($input, $DATA, $update($input?$DATA))"/>
   </xsl:function>   
   
   <xsl:function name="set:build" as="set:atomic-set" visibility="public">
      <xsl:param name="values" as="xs:anyAtomicType*" default="()"/>
      <xsl:record as="set:atomic-set"
         _data=
            "map:build($values, values:=true#0, {'duplicates': 'use-first'})"
         size=
            "%method fn() as xs:integer 
                     { map:size(?$DATA) }"
         empty=
            "%method fn() as xs:boolean 
                     { map:empty(?$DATA) }"
         contains=
            "%method fn($value as xs:anyAtomicType) as xs:boolean 
                     { map:contains(?$DATA, $value) }"
         contains-all=
            "%method fn($other as set:atomic-set) as xs:boolean 
                     { every($value, map:contains(?$DATA, ?)) }"
         values=
            "%method fn() as xs:anyAtomicType* 
                     { keys(?$DATA) }"
         add=
            "%method fn($value as xs:anyAtomicType) as xs:anyAtomicType*
                     { set:replaceData(., map:put(?, $value, true())) }"
         remove=
            "%method fn($value as xs:anyAtomicType) as xs:anyAtomicType* 
                     { set:replaceData(., map:remove(?, $value)) }"
         union=
            "%method fn($other as set:atomic-set) as set:atomic-set 
                     { set:replaceData(., fn($this) {map:merge(($this, $other?$DATA),
                                                    {'duplicates': 'use-first'})})
                     }"
         intersect=
            "%method fn($other as set:atomic-set) as set:atomic-set 
                     { set:replaceData(., map:filter(?, $other?contains)) }"
         except=
            "%method fn($other as set:atomic-set) as set:atomic-set 
                     { set:replaceData(., map:remove(?, $other?values())) }"/>
      
   </xsl:function>
   
   
</xsl:package>
Editorial note 

The example is not yet tested. It could also be written using the new xsl:record instruction, which might be more concise.