Reputation: 501
I want to use new feature in XPath3.1 like array
and map
, This might sound like a googleable question, but I try many example code still receive error message, here's how I got so far:
<!-- XSLT.xslt -->
<!-- using SaxonHE9-8-0-7 -->
<xsl:stylesheet version="3.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text" indent="yes"/>
<xsl:template match="/">
<xsl:copy-of select="system-property('xsl:version ')"/> <!-- show 3.0 -->
<xsl:copy-of select="system-property('xsl:vendor ')"/> <!-- show Saxonica -->
<xsl:copy-of select="system-property('xsl:xpath-version')"/> <!-- show 3.1 -->
<xsl:copy-of select="system-property('xsl:xsd-version ')"/> <!-- show 1.1 -->
</xsl:template>
</xsl:stylesheet>
So it's there a simple and working code that can demonstrate the power of array
and map
? thanks!
Upvotes: 1
Views: 6678
Reputation: 167401
Maps can help primarily with two issues:
As for simple examples, the various sections in the specs on the map and array syntax and the functions have simple examples, to use the functions with XSLT you only have to make sure you declare the namespaces the functions are defined in with e.g.
xmlns:map="http://www.w3.org/2005/xpath-functions/map"
xmlns:array="http://www.w3.org/2005/xpath-functions/array"
I am not sure simple examples can demonstrate the power of a new language feature and maps and arrays have various features, to give you one example that transforms some XML input to JSON by creating maps (in one template using the XSLT xsl:map
and in another the XPath map constructor syntax) and arrays and serializing the result as json
:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:math="http://www.w3.org/2005/xpath-functions/math"
xmlns:map="http://www.w3.org/2005/xpath-functions/map"
xmlns:array="http://www.w3.org/2005/xpath-functions/array"
exclude-result-prefixes="xs math map array"
version="3.0">
<xsl:output method="json" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="root">
<xsl:map>
<xsl:map-entry key="local-name()">
<xsl:apply-templates/>
</xsl:map-entry>
</xsl:map>
</xsl:template>
<xsl:template match="items">
<xsl:variable name="items" as="item()*">
<xsl:apply-templates/>
</xsl:variable>
<xsl:sequence select="map { local-name() : array { $items }}"/>
</xsl:template>
<xsl:template match="item">
<xsl:sequence select="map { 'foo' : xs:integer(foo), 'bar' : string(bar) }"/>
</xsl:template>
</xsl:stylesheet>
So that transforms
<root>
<items>
<item>
<foo>1</foo>
<bar>a</bar>
</item>
<item>
<foo>2</foo>
<bar>b</bar>
</item>
</items>
</root>
to the JSON
{
"root": {
"items": [
{
"foo":1,
"bar":"a"
},
{
"foo":2,
"bar":"b"
}
]
}
}
Online at http://xsltfiddle.liberty-development.net/b4GWV3.
I have also put together another sample at http://xsltfiddle.liberty-development.net/6qM2e27 that shows some ways to construct maps:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:math="http://www.w3.org/2005/xpath-functions/math"
xmlns:map="http://www.w3.org/2005/xpath-functions/map"
xmlns:array="http://www.w3.org/2005/xpath-functions/array"
exclude-result-prefixes="xs math map array"
version="3.0">
<xsl:output method="adaptive" indent="yes"/>
<!-- a map constructed with XPath 3.1 map constructor https://www.w3.org/TR/xpath-31/#id-map-constructors -->
<xsl:param name="map1" as="map(*)" select="map { 'string' : 'foo', 'number' : 3.14, 'boolean' : true(), 'sequence' : (1, 2, 3) }"/>
<xsl:param name="json-string" as="xs:string">
{
"foo" : "value 1",
"bar" : 3.14,
"baz" : true,
"items" : ["a", "b", "c" ]
}
</xsl:param>
<!-- a map constructed from a string containing JSON using the parse-json function https://www.w3.org/TR/xpath-functions/#func-parse-json -->
<xsl:param name="map2" as="map(*)" select="parse-json($json-string)"/>
<!-- a map constructed using the XSLT xsl:map and xsl:map-entry instructions https://www.w3.org/TR/xslt-30/#map-instructions -->
<xsl:param name="map3" as="map(*)">
<xsl:map>
<xsl:map-entry key="'key1'" select="'value 1'"/>
<xsl:map-entry key="'x'" select="3.1415927"/>
</xsl:map>
</xsl:param>
<!-- a map constructed by merging several maps using the map:merge function https://www.w3.org/TR/xpath-functions/#func-map-merge -->
<xsl:param name="map4" as="map(*)" select="map:merge(($map1, $map2, $map3))"/>
<!-- a map constructed by putting a new value into an existing map using the map:put function https://www.w3.org/TR/xpath-functions/#func-map-put -->
<xsl:param name="map5" as="map(*)" select="map:put($map1, 'new-key', 'new value')"/>
<xsl:template match="/">
<xsl:sequence select="$map1, $map2, $map3, $map4, $map5"/>
</xsl:template>
</xsl:stylesheet>
Output of this is (unfortunately there is currently no way in Saxon to pretty print/indent items with output method 'adaptive'):
map{"sequence":(1,2,3),"boolean":true(),"number":3.14,"string":"foo"}
map{"items":["a","b","c"],"foo":"value 1","bar":3.14e0,"baz":true()}
map{"key1":"value 1","x":3.1415927}
map{"items":["a","b","c"],"sequence":(1,2,3),"foo":"value 1","boolean":true(),"number":3.14,"string":"foo","key1":"value 1","bar":3.14e0,"x":3.1415927,"baz":true()}
map{"sequence":(1,2,3),"boolean":true(),"number":3.14,"string":"foo","new-key":"new value"}
And a similar example showing some basic variants to construct arrays is
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:math="http://www.w3.org/2005/xpath-functions/math"
xmlns:map="http://www.w3.org/2005/xpath-functions/map"
xmlns:array="http://www.w3.org/2005/xpath-functions/array"
exclude-result-prefixes="xs math map array"
version="3.0">
<xsl:output method="adaptive" indent="yes"/>
<!-- array created with square array constructor https://www.w3.org/TR/xpath-31/#prod-xpath31-SquareArrayConstructor -->
<xsl:param name="array1" as="array(xs:integer)" select="[1, 2, 3, 4]"/>
<!-- array created with curly braces array constructor https://www.w3.org/TR/xpath-31/#prod-xpath31-CurlyArrayConstructor -->
<xsl:param name="array2" as="array(xs:string)" select="array{ (string-to-codepoints('A') to string-to-codepoints('E'))!codepoints-to-string(.) }"/>
<!-- if we use the square array constructor with an expression like above inside we get an array containing a sequence of strings -->
<xsl:param name="array3" as="array(xs:string*)" select="[ (string-to-codepoints('A') to string-to-codepoints('E'))!codepoints-to-string(.) ]"/>
<xsl:param name="json-array-as-string" as="xs:string">
[ { "foo" : 1 }, { "foo" : 2 }, { "foo" : 3 } ]
</xsl:param>
<!-- array constructed by parsing JSON input with function parse-json https://www.w3.org/TR/xpath-functions/#func-parse-json -->
<xsl:param name="array4" as="array(map(xs:string, xs:double))" select="parse-json($json-array-as-string)"/>
<!-- array constructed by joining two arrays using the function array:join https://www.w3.org/TR/xpath-functions/#func-array-join -->
<xsl:param name="array5" as="array(*)" select="array:join(($array1, $array2))"/>
<!-- array constructed by extracting subarray with function array:subarray https://www.w3.org/TR/xpath-functions/#func-array-subarray -->
<xsl:param name="array6" as="array(*)" select="array:subarray($array5, 3, 4)"/>
<xsl:template match="/">
<xsl:sequence select="$array1, $array2, $array3, $array4, $array5, $array6"/>
</xsl:template>
</xsl:stylesheet>
which outputs
[1,2,3,4]
["A","B","C","D","E"]
[("A","B","C","D","E")]
[map{"foo":1.0e0},map{"foo":2.0e0},map{"foo":3.0e0}]
[1,2,3,4,"A","B","C","D","E"]
[3,4,"A","B"]
http://xsltfiddle.liberty-development.net/eiQZDbd
At this stage of the publication, it might also be worth looking at the various test cases in the XSLT 3 and the combined XPath and XQuery test suites, for instance:
xsl:map
with streaming xml-to-json
json-to-xml
test cases.Upvotes: 10