xsl-list
[Top] [All Lists]

Re: [xsl] Finding Only Initial Following Siblings That Meet Some Criteria

2020-02-05 17:07:27
Grouping should liberate you from looking ahead or behind. So instead of matching the first <ph outputclass="x">, you'd match <p> (or more generally '*[ph[@outputclass]]') and do the group-adjacent grouping for the child nodes, like this:

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform";
  version="3.0">
  <xsl:template match="*[ph[@outputclass]]">
    <xsl:copy>
      <xsl:apply-templates select="@*" mode="#current"/>
      <xsl:for-each-group select="node()"
        group-adjacent="string(self::ph/@outputclass)">
        <xsl:choose>
          <xsl:when test="current-grouping-key()">
            <xsl:element name="{current-grouping-key()}">
              <xsl:value-of select="current-group()"
                separator=""/>
            </xsl:element>
          </xsl:when>
          <xsl:otherwise>
            <xsl:apply-templates select="current-group()"
              mode="#current"/>
          </xsl:otherwise>
        </xsl:choose>
      </xsl:for-each-group>
    </xsl:copy>
  </xsl:template>

  <xsl:mode on-no-match="shallow-copy"/>
</xsl:stylesheet>

This is not shorter in terms of lines of code than what you suggested. In terms of performance, it could be a bit more efficient than your solution, depending on the cost of identifying the first ph[@output-class] and its following siblings, compared to the cost of identifying a parent of ph[@output-class] and selecting its children.

But as I wanted to say above, in terms of idiomatic XSLT 2+ purity, I'd always prefer a solution that doesn't look along the preceding/following axes, even when it is done just once for selecting the for-each-group population.

Gerrit

On 05.02.2020 23:29, Eliot Kimber ekimber(_at_)contrext(_dot_)com wrote:
In my XML I can have adjacent elements that should be processed as a unit, 
where the adjacent elements all have the same value for a given attribute. 
Other elements with the same attribute could be following siblings but 
separated by other elements or text nodes, i.e.:

<p>Text <ph outputclass="x">1</ph><ph outputclass="x">2</ph> more text <ph 
outputclass="x">New sequence</ph></p>

Where the rendered result should combine the first two <ph> elements but not 
the third, i.e.:

<p>Text <x>12</x> more text <x>New sequence</x></p>

Processing is applied to the first element in the document with the @outputclass value 
"x" and then I want to grab any immediately following siblings with the same 
@outputclass value and no intervening text or element nodes.

My solution is to use for-each-group like so:

     <xsl:variable name="this" as="element()" select="."/>
     <xsl:variable name="adjacent-sibs" as="element()+">
       <xsl:for-each-group select="($this, $this/following-sibling::node())"
         group-adjacent="string(@outputclass)">
         <xsl:if test=". is $this">
           <xsl:sequence select="current-group()"/>
         </xsl:if>
       </xsl:for-each-group>
     </xsl:variable>

Which works, but I'm thinking there must be a more compact way to do the same 
selection, but the formulation is escaping me.

Is there a more compact or more efficient way to make this selection of only 
immediately-adjacent following siblings?

Thanks,

E.
--
Eliot Kimber
http://contrext.com
--~----------------------------------------------------------------
XSL-List info and archive: http://www.mulberrytech.com/xsl/xsl-list
EasyUnsubscribe: http://lists.mulberrytech.com/unsub/xsl-list/1167547
or by email: xsl-list-unsub(_at_)lists(_dot_)mulberrytech(_dot_)com
--~--

<Prev in Thread] Current Thread [Next in Thread>