Skip to content

Instantly share code, notes, and snippets.

@fancellu
Last active November 27, 2025 21:22
Show Gist options
  • Select an option

  • Save fancellu/7e9da4d88b550e9835a77c6605c06d21 to your computer and use it in GitHub Desktop.

Select an option

Save fancellu/7e9da4d88b550e9835a77c6605c06d21 to your computer and use it in GitHub Desktop.
Little XSLT 4.0 example using keys, maps, arrays
{
"items": [
{ "product":"Laptop", "qty":1, "line_total":1200 },
{ "product":"Mouse", "qty":2, "line_total":100 },
{ "error":"Product not found: p99", "product":"UNKNOWN", "qty":1, "line_total":0 }
],
"total_cost": 1300,
"order_id": "ord-666",
"tax_rate": 0
}
{
"items": [
{ "product":"Laptop", "qty":1, "line_total":1200 },
{ "product":"Mouse", "qty":2, "line_total":100 }
],
"total_cost": 1586,
"order_id": "ord-555",
"tax_rate": 0.22
}
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:map="http://www.w3.org/2005/xpath-functions/map"
xmlns:array="http://www.w3.org/2005/xpath-functions/array"
exclude-result-prefixes="#all"
version="4.0">
<xsl:output method="json" indent="yes"/>
<xsl:key name="product-lookup" match="products/item" use="@id"/>
<xsl:variable name="tax-rates" as="map(xs:string, xs:decimal)">
<xsl:map>
<xsl:map-entry key="'US'" select="0.08"/>
<xsl:map-entry key="'EU'" select="0.20"/>
<xsl:map-entry key="'UK'" select="0.22"/>
</xsl:map>
</xsl:variable>
<xsl:template match="/">
<xsl:variable name="order" select="root/order"/>
<xsl:map>
<xsl:map-entry key="'order_id'" select="string($order/@id)"/>
<xsl:variable name="tax" as="xs:decimal"
select="(map:get($tax-rates, string($order/@region)), 0.0)[1]"/>
<xsl:map-entry key="'tax_rate'" select="$tax"/>
<xsl:map-entry key="'items'">
<xsl:array>
<xsl:apply-templates select="$order/line"/>
</xsl:array>
</xsl:map-entry>
<xsl:map-entry key="'total_cost'">
<xsl:variable name="safe-line-totals" as="xs:decimal*">
<xsl:for-each select="$order/line">
<xsl:try>
<xsl:sequence select="xs:decimal(key('product-lookup', @product-id)/@price) * xs:integer(@qty)"/>
<xsl:catch>
<xsl:sequence select="0"/>
</xsl:catch>
</xsl:try>
</xsl:for-each>
</xsl:variable>
<xsl:sequence select="sum($safe-line-totals) * (1 + $tax)"/>
</xsl:map-entry>
</xsl:map>
</xsl:template>
<xsl:template match="line">
<xsl:variable name="prod" select="key('product-lookup', @product-id)"/>
<xsl:map>
<xsl:map-entry key="'product'"
select="if (exists($prod)) then string($prod) else 'UNKNOWN'"/>
<xsl:map-entry key="'qty'" select="xs:integer(@qty)"/>
<xsl:map-entry key="'line_total'">
<xsl:try>
<xsl:variable name="calc" select="xs:decimal($prod/@price) * xs:integer(@qty)"/>
<xsl:sequence select="($calc, 0)[1]"/>
<xsl:catch>
<xsl:sequence select="0"/>
</xsl:catch>
</xsl:try>
</xsl:map-entry>
<xsl:if test="empty($prod)">
<xsl:map-entry key="'error'" select="concat('Product not found: ',@product-id) "/>
</xsl:if>
</xsl:map>
</xsl:template>
</xsl:stylesheet>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:map="http://www.w3.org/2005/xpath-functions/map"
xmlns:array="http://www.w3.org/2005/xpath-functions/array"
exclude-result-prefixes="#all"
version="4.0">
<xsl:output method="json" indent="yes"/>
<xsl:mode streamable="yes" use-accumulators="product-cache running-total"/>
<xsl:accumulator name="product-cache" as="map(xs:string, xs:decimal)"
initial-value="map{}" streamable="yes">
<xsl:accumulator-rule match="products/item"
select="map:put($value, string(@id), xs:decimal(@price))"/>
</xsl:accumulator>
<xsl:accumulator name="running-total" as="xs:decimal"
initial-value="0.0" streamable="yes">
<xsl:accumulator-rule match="order" select="0.0"/>
<xsl:accumulator-rule match="line">
<xsl:variable name="prod-map" select="accumulator-before('product-cache')"/>
<xsl:variable name="price" select="($prod-map(string(@product-id)), 0)[1]"/>
<xsl:sequence select="$value + ($price * xs:integer(@qty))"/>
</xsl:accumulator-rule>
</xsl:accumulator>
<xsl:variable name="tax-rates" as="map(xs:string, xs:decimal)">
<xsl:map>
<xsl:map-entry key="'US'" select="0.08"/>
<xsl:map-entry key="'EU'" select="0.20"/>
<xsl:map-entry key="'UK'" select="0.22"/>
</xsl:map>
</xsl:variable>
<xsl:template match="/">
<xsl:apply-templates select="root/order"/>
</xsl:template>
<xsl:template match="order">
<xsl:map>
<xsl:map-entry key="'order_id'" select="string(@id)"/>
<xsl:variable name="tax" as="xs:decimal"
select="(map:get($tax-rates, string(@region)), 0.0)[1]"/>
<xsl:map-entry key="'tax_rate'" select="$tax"/>
<xsl:map-entry key="'items'">
<xsl:array>
<xsl:apply-templates select="line"/>
</xsl:array>
</xsl:map-entry>
<xsl:map-entry key="'total_cost'">
<xsl:variable name="subtotal" select="accumulator-after('running-total')"/>
<xsl:sequence select="$subtotal * (1 + $tax)"/>
</xsl:map-entry>
</xsl:map>
</xsl:template>
<xsl:template match="line">
<xsl:variable name="prod-map" select="accumulator-before('product-cache')"/>
<xsl:variable name="price" select="($prod-map(string(@product-id)), 0)[1]"/>
<xsl:map>
<xsl:map-entry key="'product_id'" select="string(@product-id)"/>
<xsl:map-entry key="'qty'" select="xs:integer(@qty)"/>
<xsl:map-entry key="'line_total'" select="$price * xs:integer(@qty)"/>
</xsl:map>
</xsl:template>
</xsl:stylesheet>
<root>
<products>
<item id="p1" price="1200.00">Laptop</item>
<item id="p2" price="50.00">Mouse</item>
</products>
<order id="ord-666" region="MARS">
<line product-id="p1" qty="1"/>
<line product-id="p2" qty="2"/>
<line product-id="p99" qty="1"/> </order>
</root>
<root>
<products>
<item id="p1" price="1200.00">Laptop</item>
<item id="p2" price="50.00">Mouse</item>
</products>
<order id="ord-555" region="UK">
<line product-id="p1" qty="1"/>
<line product-id="p2" qty="2"/>
</order>
</root>
{
"items": [
{ "product_id":"p1", "qty":1, "line_total":1200 },
{ "product_id":"p2", "qty":2, "line_total":100 },
{ "product_id":"p99", "qty":1, "line_total":0 }
],
"total_cost": 1300,
"order_id": "ord-666",
"tax_rate": 0
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment