Here's a fun list of new features in the XPath 4.0, XQuery 4.0, and XSLT 4.0, that are available to play with in Saxon 13.
-
Select an attribute, using a default value if absent
@discount otherwise 0 -
Use a conditional with no else branch
if (@discount) { " (reduced!)" } -
Shorten
functiontofnfor-each(//employee, fn($e){$e/@ssn}) -
Use the context item to simplify arity-1 functions
for-each(//employee, fn{@ssn}) -
Drop the
mapkeyword from map constructorslet $map := {'x':1, 'y':2} -
Include conditional entries in a map constructor
{'x': +@x, 'y': +@y, if (@z) {{'z': +@z}}} -
Include multiple entries in a map constructor
{//data ! {@key : string(@value)}} -
Build a map from a sequence
map:build(//employee, fn{@ssn}) -
Select a range of entries from a sequence
$input[1 to 5] -
Start talking about atomic items rather than atomic values
-
Use QName literals
node-name($node) = #xml:space -
Use underscore in numeric literals
let $million := 1_000_000 -
Use hex literals
let $mask := 0xffff -
Use alternative names in an element test
//element(chapter|appendix) -
Declare namespaces in XPath
declare namespace p = "http:/my.ns"; //p:table -
Declare a default namespace in XPath
declare default element namespace "http:/my.ns"; //table -
Use
##anyto match elements by local name, ignoring namespacedeclare default element namespace "##any"; //table -
Use string templates
`{$firstname} {$lastname}` -
Use × and ÷ for multiplication and division
@price × 1.2 -
Convert untyped atomic values implicitly to decimal where appropriate
<a>1.1</a> = 1.1 -
Write
<<and>>asprecedesandfollowspara[. precedes $first-heading] -
Compare nodes using
is-not*[. is-not $first-heading] -
Use the
=!>operator to apply a function to all items in a sequencetokenize($str) =!> upper-case() -
Use the pipeline operator
->to set the context item for an expression$employee -> `First name: {@first} Last name: {@last}` -
Use a multi-character representation of "percent" in a decimal format
percent = "%:pc" -
Start talking about coercion rules rather than function conversion rules
-
Take advantage of down-casting in the coercion rules:
declare function my:f($x as xs:positiveInteger){$x + 1}; my:f(3) -
Check that you always have a space after
"{#"in a pragma{# saxon:extension xxx #} -
Take advantage of coercion in variable declarations
let $x as xs:decimal := @price -
Declare functions with optional parameters
declare function my:f($x as xs:integer := 0) -
Use keywords in function calls
substring($value, start := 3) -
Declare functions with no namespace prefix
declare function increment($x) {$x+1}; increment(42) -
Serialize output as canonical XML
serialize($output, {'method': 'xml', canonical': true()}) -
Take advantage of the functions in the bin and file namespaces. Previously defined in EXPath, these are now integrated into the specs.
-
Watch out for decimal=double comparisons. These are now compared as decimals, not as doubles
1.1e0 != 1.1 -
Convert arbitrary XML to JSON using the element-to-map function
element-to-map(doc('books.xml')/*) -
Process CSV input files using the new
csv-docandparse-csvfunctionscsv-doc('data.csv') -
Process HTML input files
parse-html('index.html') -
Use higher-order functions knowing they will be there in all implementations. Higher-order functions are no longer an optional feature.
-
Use the second argument of callback functions to get the position of an item in a sequence
filter($input, fn{$item, $pos){$pos gt 5}) -
Get the last item of a sequence using foot()
foot($input) -
Get selected items in a sequence using slice()
slice($in, start := 5, end := 2, step := -2) -
Use the
deep-equal()function with options to control the comparisondeep-equal($p1, $p2, {'whitespace': 'normalize'}) -
Use the
all-different()function to check uniqueness of valuesall-different(//test-case/@name) -
Use the
highest()andlowest()functions to find the items with the minimum or maximum value of some propertyhighest(//employee, fn{@salary}) -
Use the
index-where()function to find the positions of all items with some propertyindex-where(*, fn{exists(self::h2)}) -
Use the
partition()function to perform positional groupingpartition(*, fn($group, $next){exists($next[self::h2])}) -
Use the
sort-by()function to sort a sequence using multiple sort keyssort-by(//person, ({'key': fn{@last}}, {'key': fn{@date-of-birth}, 'order': 'descending'})) -
Use the
sort-with()function to sort using a comparatorsort-with($input, compare#2), -
Use
take-whileto select items from a sequence until some condition is falsetake-while(*, fn{not(self::h2)}) -
Use
is-NaNto check whether an input item is NaN*[not(is-NaN(.))] -
Use
round()with an explicit rounding moderound($value, precision := '2', mode := 'half-toward-zero') -
Read integers in hexadecimal notation
parse-integer("feff", 16) -
Format integers in hexadecimal notation
format-integer($value, '16^xxxxxxxx') -
Supply inline options to
format-number()format-number($value, {'decimal-separator': ',', 'grouping-separator': '.'}) -
Use new mathematical functions
math:e(),math:sinh(), etc.math:cosh(1) -
Use the new collation for Unicode case-blind comparison
compare($a, $b, "http://www.w3.org/2005/xpath-functions/collation/unicode-case-insensitive") -
Construct a collation with desired properties
compare($a, $b, collation({'lang': 'se', 'numeric': true()})) -
Get the character with a given codepoint
char(0xA0) -
Get the character with a given HTML entity name
char("nbsp") -
Get all the characters in a string
characters($input) -
Use lookahead and lookbehind in regular expressions
matches($input, "Chapter(?=\s+[1-9])") -
Match word boundaries in a string
matches($input, "\bthe\b") -
Compute replacement values for matching substrings
replace($input, "(0-9)+", fn{.+1}) -
Split a URI into its parts, and recombine them
parse-uri(@href) => map:put('scheme', 'https') => build-uri() -
Build a date or time from its components
build-dateTime({'year': $year, 'month': $month, 'day': $day}) -
Get the civil timezone offset at a given time and place
civil-timezone(current-dateTime(), 'Europe/London') -
Use
@thenand@elseonxsl:if<xsl:if test="@type=1" then="@full-name" else="@abbreviation"/> -
Use
@selectonxsl:whenandxsl:otherwise<xsl:choose><xsl:when test="@type=1" select="'gasket'"/></xsl:choose> -
Select options using the new
xsl:switchinstruction<xsl:switch select="@type"><xsl:when test="1" select="'gasket'"/></xsl:switch> -
Provide default values for XSLT function parameters
<xsl:param name="options" as="map(*)" required="no" select="{}"/> -
Use XSLT functions in no namespace
<xsl:function name="f">...</xsl:function> -
Use
xsl:textin preference toxsl:value-of<xsl:text>{position()} of {last()}</xsl:text> -
Construct CDATA sections selectively in serialized output
<xsl:text cdata="true" select="$example"/> -
Use capturing accumulators to capture a snapshot of an element as an accumulator value
<xsl:accumulator-rule match="heading" phase="end" capture="yes" select="."/> -
Use explicit options to control XML parsing in the
doc()anddocument()functionsdoc("input.xml", {'dtd-validation': true()}) -
Build arrays in XSLT using
xsl:arrayandxsl:array-member<xsl:array select="1 to 5"/> -
Control the handling of duplicate keys in
xsl:mapwith the newduplicatesoption<xsl:map duplicates="'use-last'">...</xsl:map> -
Stop JSON output displaying
/as\/escape-solidus="no" -
Serialize a sequence of maps in json-lines format (one JSON object per line, newline separated)
json-lines="yes" -
Use different XSD schemas for validating the input and output of a transformation
<xsl:import-schema role="output"/>, <xsl:result-document schema-role="output"/> -
Write all the template rules for a given mode as children of the
xsl:modeelement<xsl:mode name="M"><xsl:template match="A"/></xsl:mode> -
In an XSLT module, link to the main module to help IDEs locate all the variable and function declarations
<xsl:transform main-module="main.xsl">...</xsl:transform> -
Use
xsl:noteelements for structured XSLT documentation<xsl:note>This is a comment</xsl:note> -
Use
fixed-namespacesto reduce the amount of boilerplate on thexsl:transformelement<xsl:transform fixed-namespaces="#standard">...</xsl:transform> -
Take advantage of new features for simplified stylesheets. The outermost element can now be any instruction, for example
xsl:result-documentorxsl:map. -
Make unprefixed name tests match elements in any namespace
xpath-default-namespace="##any" -
Declare named item types
<xsl:item-type name="binary" select="(xs:hexBinary | xs:base64Binary)"/> -
Use types in match patterns
match="~map(xs:string, xs:integer) -
Use the
separatorattribute inxsl:for-each<xsl:for-each select="$input" separator=",">...</xsl:for-each> -
Use record types to declare maps with statically known fields
as record(first as xs:string, middle as xs:string*, last as xs:string) -
Declare function parameters using choice types
as (xs:date | xs:time | xs:dateTime) -
Declare function parameters using enumeration types
as enum("red", "green", "blue") -
Use path expressions to navigate trees of maps and arrays (JNodes)
$map/authors/*[1]/name/first ! string() -
Iterate over arrays with an enhanced FLWOR expression
for member $m in $array ... return count($m) -
Iterate over maps with an enhanced FLWOR expression
for key $key value $value in $map return `{$key} = {$value}` -
Use computed node constructors in XPath
attribute #xlink:href {$target} -
Use enhanced
forandletexpressions in XPathfor $x as xs:string at $pos in $X let $r := string-length($x) + $pos return $r + 1 -
Use destructuring map assignments
let {$height, $width} := $rectangle return $height × $width -
Use destructuring array assignments
let [$first, $second, $third] := $array return ($first, $second, $third) -
Read the specifications at http://qt4cg.org/, and provide feedback at https://github.com/qt4cg/qtspecs