Toolbox
Toolbox

Reputation: 2491

Add attribute-set per queried element

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

Answers (1)

Martin Honnen
Martin Honnen

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

Related Questions