Home > other >  Wrap element(s) with a new tag determined by the total of their attribute values
Wrap element(s) with a new tag determined by the total of their attribute values

Time:09-02

When transforming the following XML instance;

source.xml

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<indd xmlns:aid="http://ns.adobe.com/AdobeInDesign/4.0/" xmlns:aid5="http://ns.adobe.com/AdobeInDesign/5.0/">
  <article>
    <table aid5:tablestyle="my-table" aid:trows="10" aid:tcols="4">
      <cell aid:theader="" aid:ccols="3">Head A</cell>
      <cell aid:theader="" aid:ccols="1">Head B</cell>
      <cell aid:ccols="2">A</cell>
      <cell aid:ccols="1">B</cell>
      <cell aid:ccols="1">C</cell>
      <cell aid:ccols="4"></cell>
      <cell aid:ccols="1">E</cell>
      <cell aid:ccols="1">F</cell>
      <cell aid:ccols="1">G</cell>
      <cell aid:ccols="1">H</cell>
      <cell aid:ccols="1">I</cell>
      <cell aid:ccols="1">J</cell>
      <cell aid:ccols="1">K</cell>
      <cell aid:ccols="1">L</cell>
      <cell aid:ccols="1">M</cell>
      <cell aid:ccols="1">N</cell>
      <cell aid:ccols="1">O</cell>
      <cell aid:ccols="1">P</cell>
      <cell aid:ccols="1">Q</cell>
      <cell aid:ccols="1">R</cell>
      <cell aid:ccols="1">S</cell>
      <cell aid:ccols="1">T</cell>
      <cell aid:ccols="1">U</cell>
      <cell aid:ccols="1">V</cell>
      <cell aid:ccols="1">W</cell>
      <cell aid:ccols="1">X</cell>
      <cell aid:ccols="1">Y</cell>
      <cell aid:ccols="3">Z</cell>
    </table>
  </article>
</indd>

using the following XSLT (1.0);

transform.xsl

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  xmlns:aid="http://ns.adobe.com/AdobeInDesign/4.0/"
  xmlns:aid5="http://ns.adobe.com/AdobeInDesign/5.0/"
  exclude-result-prefixes="aid aid5"
  version="1.0">

  <xsl:output method="xml" indent="yes" />
  <xsl:strip-space elements="*"/>

  <xsl:template match="indd/article/table[@aid5:tablestyle='my-table']">
    <table>
      <xsl:attribute name="class">
        <xsl:value-of select="@aid5:tablestyle"/>
      </xsl:attribute>

      <xsl:for-each select="cell">
        <xsl:choose>
          <xsl:when test="@aid:theader=''">
            <th>
              <xsl:attribute name="colspan">
                <xsl:value-of select="@aid:ccols"/>
              </xsl:attribute>
              <xsl:value-of select="."/>
            </th>
          </xsl:when>
          <xsl:otherwise>
            <td>
              <xsl:attribute name="colspan">
                <xsl:value-of select="@aid:ccols"/>
              </xsl:attribute>
              <xsl:value-of select="."/>
            </td>
          </xsl:otherwise>
        </xsl:choose>
      </xsl:for-each>
    </table>
  </xsl:template>

</xsl:stylesheet>

It produces the following output:

result.xml

<?xml version="1.0" encoding="UTF-8"?>
<table >
   <th colspan="3">Head A</th>
   <th colspan="1">Head B</th>
   <td colspan="2">A</td>
   <td colspan="1">B</td>
   <td colspan="1">C</td>
   <td colspan="4"/>
   <td colspan="1">E</td>
   <td colspan="1">F</td>
   <td colspan="1">G</td>
   <td colspan="1">H</td>
   <td colspan="1">I</td>
   <td colspan="1">J</td>
   <td colspan="1">K</td>
   <td colspan="1">L</td>
   <td colspan="1">M</td>
   <td colspan="1">N</td>
   <td colspan="1">O</td>
   <td colspan="1">P</td>
   <td colspan="1">Q</td>
   <td colspan="1">R</td>
   <td colspan="1">S</td>
   <td colspan="1">T</td>
   <td colspan="1">U</td>
   <td colspan="1">V</td>
   <td colspan="1">W</td>
   <td colspan="1">X</td>
   <td colspan="1">Y</td>
   <td colspan="3">Z</td>
</table>

Desired Result

However, my requirement is the transform source.xml to produce the following:

wanted.xml

<?xml version="1.0" encoding="UTF-8"?>
<table >
  <tr>
    <th colspan="3">Head A</th>
    <th colspan="1">Head B</th>
  </tr>
  <tr>
    <td colspan="2">A</td>
    <td colspan="1">B</td>
    <td colspan="1">C</td>
  </tr>
  <tr>
    <td colspan="4"/>
  </tr>
  <tr>
    <td colspan="1">E</td>
    <td colspan="1">F</td>
    <td colspan="1">G</td>
    <td colspan="1">H</td>
  </tr>
  <tr>
    <td colspan="1">I</td>
    <td colspan="1">J</td>
    <td colspan="1">K</td>
    <td colspan="1">L</td>
  </tr>
  <tr>
    <td colspan="1">M</td>
    <td colspan="1">N</td>
    <td colspan="1">O</td>
    <td colspan="1">P</td>
  </tr>
  <tr>
    <td colspan="1">Q</td>
    <td colspan="1">R</td>
    <td colspan="1">S</td>
    <td colspan="1">T</td>
  </tr>
  <tr>
    <td colspan="1">U</td>
    <td colspan="1">V</td>
    <td colspan="1">W</td>
    <td colspan="1">X</td>
  </tr>
  <tr>
    <td colspan="1">Y</td>
    <td colspan="3">Z</td>
  </tr>
</table>

Currently transform.xsl does not transform source.xml to include the necessary tr elements that are present in wanted.xml.

Question

Is it possible to use XSLT (1.0) to include the necessary tr elements as shown in wanted.xml?

As you can see, the values of the aid:ccols attributes for each cell element that is present in source.xml could be utilized to inform the transformation as to when the resultant th or td element(s) should be wrapped in tr elements. Essentially it's when consecutive aid:ccols values total 4 as per the value of the table elements aid:tcols="4" attribute in source.xml.

For example:

  • Consider the first two cell elements at positions 1 and 2 in source.xml. Their total value of aid:ccols attributes is 4 (3 1), therefore their resulant th elements should be wrapped with an opening and closing tr tag.

  • Consider cell elements in source.xml at positions 3,4, and 5. Their total value of aid:ccols attributes is 4 (2 1 1) therefore their resulant th elements should be wrapped with an opening and closing tr tag.

  • Consider the cell element in source.xml at positions 6. Its aid:ccols value already totals 4 therefore its resultant th element should be wrapped with an opening and closing tr tag.

CodePudding user response:

If I understand this correctly, you want to do something like:

XSLT 1.0

<xsl:stylesheet version="1.0" 
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:aid5="http://ns.adobe.com/AdobeInDesign/5.0/"
xmlns:aid="http://ns.adobe.com/AdobeInDesign/4.0/"
exclude-result-prefixes="aid5 aid">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:strip-space elements="*"/>

<xsl:template match="table">
    <table >
        <tr>
            <xsl:for-each select="cell[@aid:theader]">
                <th colspan="{@aid:ccols}">
                    <xsl:value-of select="."/>
                </th>
            </xsl:for-each>
        </tr>
        <xsl:call-template name="body">
            <xsl:with-param name="cells" select="cell[not(@aid:theader)]"/>
            <xsl:with-param name="target-width" select="@aid:tcols"/>
        </xsl:call-template>
    </table>
</xsl:template>

<xsl:template name="body">
    <xsl:param name="cells"/>
    <xsl:param name="target-width"/>
    <xsl:param name="row" select="/.."/>
    <xsl:param name="width" select="0"/>
    <xsl:choose>
        <xsl:when test="$width >= $target-width">
            <tr>
                <xsl:for-each select="$row">
                    <td colspan="{@aid:ccols}">
                        <xsl:value-of select="."/>
                    </td>
                </xsl:for-each>
            </tr>
            <!-- recursive call -->
            <xsl:call-template name="body">
                <xsl:with-param name="cells" select="$cells"/>
                <xsl:with-param name="target-width" select="$target-width"/>
                <xsl:with-param name="row" select="/.."/>
                <xsl:with-param name="width" select="0"/>
            </xsl:call-template>
        </xsl:when>
        <xsl:when test="$cells">
            <xsl:variable name="cell" select="$cells[1]" />
            <xsl:call-template name="body">
                <xsl:with-param name="cells" select="$cells[position() > 1]"/>
                <xsl:with-param name="target-width" select="$target-width"/>
                <xsl:with-param name="row" select="$row | $cell"/>
                <xsl:with-param name="width" select="$width   $cell/@aid:ccols"/>
            </xsl:call-template>
        </xsl:when>
    </xsl:choose>
</xsl:template>

</xsl:stylesheet>

Another possible approach is a technique known as "sibling recursion" (look it up). Though the basic principle is same.

  • Related