Reputation: 2491
I am working with a flat JSON source file, from a hierarchical tree perspective. As part of XSLT transformation I am looking to define groups that indicate which element should have certain attribute-set(s). The element order of the output is not important.
This is a minimized version of my code, so there is a reason why I would like to gather all settings/configuration/defitions in the top of the XSL. If the code would be very small I assume it would make sense to just add the requested elements directly inside each template.
I am aware the the "if" element lines are commented-out and that they might be needed (and expanded) with correct syntax in order for the solution to work.
If there is another better and simpler way of defining the groups that is fine also.
JSON:
<data>
{
"flat": {
"Milk": 12,
"Duck": 32,
"Beer": 8,
"Cow": 43
}
}
</data>
XSL:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:transform version="3.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:item="http://www.example.com/1"
xmlns:inventory="http://www.example.com/2"
expand-text="yes"
>
<xsl:output method="xml" indent="yes"/>
<xsl:mode on-no-match="shallow-skip"/>
<!-- Categorization -->
<xsl:variable name="group-animals">Cow, Duck</xsl:variable>
<xsl:variable name="group-beverage">Milk, Beer</xsl:variable>
<!-- Variations of attribute settings -->
<xsl:attribute-set name="set-attributes-for-category-animals">
<xsl:attribute name="contextRef">animals</xsl:attribute>
</xsl:attribute-set>
<xsl:attribute-set name="set-attributes-for-category-beverage">
<xsl:attribute name="contextRef">beverage</xsl:attribute>
</xsl:attribute-set>
<!-- Parse JSON to XML -->
<xsl:template match="data">
<inventory>
<xsl:apply-templates select="json-to-xml(.)/*"/>
</inventory>
</xsl:template>
<!-- Template -->
<!-- Planned logic: -->
<!--
If element defined in "group-animals" exist in JSON / XML map",
then use "set-attributes-for-category-animals"
If element defined in "group-beverage" exist in JSON / XML map",
then use "set-attributes-for-category-beverage"
-->
<xsl:template match="*[@key ='flat']">
<xsl:for-each select="*">
<!-- <xsl:if test=""> -->
<xsl:element
name="item:{@key}"
use-attribute-sets="set-attributes-for-category-animals"
>
<xsl:value-of select="text()"/>
</xsl:element>
<!-- </xsl:if> -->
</xsl:for-each>
</xsl:template>
</xsl:transform>
Result:
<?xml version="1.0" encoding="UTF-8"?>
<inventory xmlns:inventory="http://www.example.com/3"
xmlns:item="http://www.example.com/1">
<item:Milk contextRef="animals">12</item:Milk>
<item:Duck contextRef="animals">32</item:Duck>
<item:Beer contextRef="animals">8</item:Beer>
<item:Cow contextRef="animals">43</item:Cow>
</inventory>
Wanted result:
<?xml version="1.0" encoding="UTF-8"?>
<inventory xmlns:inventory="http://www.example.com/3"
xmlns:item="http://www.example.com/1">
<item:Milk contextRef="beverage">12</item:Milk>
<item:Duck contextRef="animals">32</item:Duck>
<item:Beer contextRef="beverage">8</item:Beer>
<item:Cow contextRef="animals">43</item:Cow>
</inventory>
Upvotes: 0
Views: 57
Reputation: 167716
I would tokenize the comma separated values into sequences of strings and then compare the key e.g.
<?xml version="1.0" encoding="UTF-8"?>
<xsl:transform version="3.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:item="http://www.example.com/1"
xmlns:inventory="http://www.example.com/2"
expand-text="yes"
>
<xsl:output method="xml" indent="yes"/>
<xsl:mode on-no-match="shallow-skip"/>
<!-- Categorization -->
<xsl:param name="group-animals">Cow, Duck</xsl:param>
<xsl:param name="animals" select="tokenize($group-animals, ',\s*')"/>
<xsl:param name="group-beverage">Milk, Beer</xsl:param>
<xsl:param name="beverages" select="tokenize($group-beverage, ',\s*')"/>
<!-- Variations of attribute settings -->
<xsl:attribute-set name="set-attributes-for-category-animals">
<xsl:attribute name="contextRef">animals</xsl:attribute>
</xsl:attribute-set>
<xsl:attribute-set name="set-attributes-for-category-beverage">
<xsl:attribute name="contextRef">beverage</xsl:attribute>
</xsl:attribute-set>
<!-- Parse JSON to XML -->
<xsl:template match="data">
<inventory>
<xsl:apply-templates select="json-to-xml(.)/*"/>
</inventory>
</xsl:template>
<!-- Template -->
<!-- Planned logic: -->
<!--
If element defined in "group-animals" exist in JSON / XML map",
then use "set-attributes-for-category-animals"
If element defined in "group-beverage" exist in JSON / XML map",
then use "set-attributes-for-category-beverage"
-->
<xsl:template match="*[@key ='flat']/*[@key = $animals]">
<xsl:element
name="item:{@key}"
use-attribute-sets="set-attributes-for-category-animals">
<xsl:value-of select="."/>
</xsl:element>
</xsl:template>
<xsl:template match="*[@key ='flat']/*[@key = $beverages]">
<xsl:element
name="item:{@key}"
use-attribute-sets="set-attributes-for-category-beverage">
<xsl:value-of select="."/>
</xsl:element>
</xsl:template>
</xsl:transform>
A general value comparison with e.g. =
is an existential comparison so @key = $animals
is true if the attribute value of the key
attribute is equal to at least one string value in the sequence of strings $animals
.
The whole match matches child elements of a parent with the key
attribute value flat
where the child's key
value is in the sequence of $animals
.
Upvotes: 3