Search code examples
xmlxsltxslt-2.0

Fetching text in between curly braces in the following element and combining text using xslt


I want to fetch the text in between curly braces and combining the hole text into one element

My XML Input is inside one td, r and t element contains text that text need to be combined in one t element with in one td:

<topic>
<body>
<table>
<tr><td><p id="1"><pPr><pstyle class="normal"/></pPr>
<r><t>{</t></r>
<r><t>Ram</t></r>
<r><t>Ramt}</t></r>
</p></td></tr>
<tr><td><p id="2"><pPr><pstyle class="normal"/></pPr>
<r><t>{Red</t></r>
<r><t>Sham</t></r>
<r><t>}</t></r>
</p></td></tr>
<tr><td><p id="3"><pPr><pstyle class="normal"/></pPr>
<r><t>{Red</t></r>
<r><t>}</t></r>
</p></td></tr>
<tr><td><p id="4"><pPr><pstyle class="normal"/></pPr>
<r><t>{Damn}</t></r>
</p></td></tr>
</table>
</body>
</topic>

XSLT I have used to fetch the curly braces text checked for staring and ending of curly then applied templates:

<?xml version="1.0" encoding="UTF-8" ?>
<xsl:transform>
<xsl:template match="@*|node()">
        <xsl:copy>
            <xsl:apply-templates select="@*|node()"/>
        </xsl:copy>
    </xsl:template>

    <xsl:template match="r/t">
        <xsl:choose>
            <xsl:when test="starts-with(t, '{') and ends-with(t, '}')">
                <r><t><xsl:apply-templates/></t></r>
            </xsl:when>
            <xsl:otherwise>
                <xsl:apply-templates/>
            </xsl:otherwise>
        </xsl:choose>
    </xsl:template>
</xsl:transform>

I am getting the output as only r elements values with out combining and t element also not existing

<topic>
<body>
<table>
<tr><td><p id="1"><pPr>
<pstyle class="normal"></pstyle>
</pPr>
<r>{</r>
<r>Ram</r>
<r>Ramt}</r>
</p></td></tr>
<tr><td><p id="2"><pPr><pstyle class="normal"></pstyle></pPr>
<r>{Red</r>
<r>Sham</r>
<r>}</r>
</p></td></tr>
<tr><td><p id="3"><pPr><pstyle class="normal"></pstyle></pPr>
<r>{Red</r>
<r>}</r>
</p></td></tr>
<tr><td><p id="4"><pPr><pstyle class="normal"></pstyle></pPr>
<r>{Damn}</r>
</p></td></tr>
</table>
</body>
</topic>

Output Needed as combining in element text which is inside r element:

<topic>
<body>
<table>
<tr><td><p id="1"><pPr><pstyle class="normal"/></pPr>
<r><t>{RamRamt}</t></r>
</p></td></tr>
<tr><td><p id="2"><pPr><pstyle class="normal"/></pPr>
<r><t>{RedSham}</t></r>
</p></td></tr>
<tr><td><p id="3"><pPr><pstyle class="normal"/></pPr>
<r><t>{Red}</t></r>
</p></td></tr>
<tr><td><p id="4"><pPr><pstyle class="normal"/></pPr>
<r><t>{Damn}</t></r>
</p></td></tr>
</table>
</body>
</topic>

Please advise


Solution

  • Here is an attempt using for-each-group:

      <xsl:template match="p[r/t]">
          <xsl:copy>
              <xsl:apply-templates select="@*"/>
              <xsl:for-each-group select="*" group-adjacent="boolean(self::r[t])">
                  <xsl:choose>
                      <xsl:when test="current-grouping-key()">
                          <xsl:for-each-group select="current-group()" group-starting-with="r[t[starts-with(., '{')]]">
                              <xsl:for-each-group select="current-group()" group-ending-with="r[t[ends-with(., '}')]]">
                                  <r><t><xsl:apply-templates select="current-group()/t/text()"/></t></r>
                              </xsl:for-each-group>
                          </xsl:for-each-group>
                      </xsl:when>
                      <xsl:otherwise>
                          <xsl:apply-templates select="current-group()"/>
                      </xsl:otherwise>
                  </xsl:choose>
              </xsl:for-each-group>
          </xsl:copy>
      </xsl:template>
    

    Assumes the identity transformation is set up as the base transformation step e.g. with <xsl:mode on-no-match="shallow-copy"/> in XSLT 3 (https://xsltfiddle.liberty-development.net/94AbWB4) or with an equivalent template spelling it out.

    The above will not cover all edge cases but you haven't really spelled out whether there can be other content; it should be possible to tune the grouping conditions and/or the inner choose/when tests to improve the code to handle more variable content.