开发者

xsl - grouping nodes based on attributes between 2 nodes

开发者 https://www.devze.com 2023-02-23 01:38 出处:网络
In XSL 1.0, I have had a search and have found similar items around grouping but I think this is slightly different. Apologies if this has already been covered I haven\'t been able to find the answer

In XSL 1.0, I have had a search and have found similar items around grouping but I think this is slightly different. Apologies if this has already been covered I haven't been able to find the answer

Input

<?xml version="1.0"?>
<xmldoc>
<section paragraphMarker="true">Part 1. </section>
<section paragraphMarker="false">Part 2. </section>
<section paragraphMarker="false">Part 3. </section>
<section paragraphMarker="true">Part 4. </section>
<section paragraphMarker="true">Part 5. </section>
<section paragraphMarker="false">Part 6. </section>
</xmldoc>

Desired Output

<p>Part 1. Part 2. Part 3.</p>
<p>Part 4. </p>
<p>Part 5. Part 6. </p>

I have tried the following:-

<xsl:key name="sectionsWithParagraphMarker" 
    match="section[@paragraphMarker='true']" use="."/>

<xsl:template match="/">
&开发者_开发知识库lt;xsl:for-each select=
    "/xmldoc/section[generate-id()
         = 
    generate-id(key('sectionsWithParagraphMarker',.)[1])]">
   <p>
<xsl:apply-templates select="."/>
<xsl:apply-templates select="./following-sibling::node()
         [count(. | /xmldoc/section[@paragraphMarker='true'][1]/
             preceding-sibling::node())
         =
         count(/xmldoc/section[@paragraphMarker='true'][1]/
             preceding-sibling::node())
         ]"/>  
        </p>
    </xsl:for-each>
</xsl:template>

<xsl:template match="section">
    <xsl:select value-of="."/>
</xsl:template>

This isn't working and I have got stuck with it. It's selecting too many 'section' nodes for all groups. Any help would be gratefully received!


<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:msxsl="urn:schemas-microsoft-com:xslt" exclude-result-prefixes="msxsl">
    <xsl:output omit-xml-declaration="yes" indent="yes"/>
    <xsl:strip-space elements="*"/>

    <xsl:template match="section">
        <xsl:if test="@paragraphMarker='true'">
            <p>
                <xsl:apply-templates select="." mode="text" />
            </p>
        </xsl:if>
    </xsl:template>

    <xsl:template match="section" mode="text">
        <xsl:value-of select="." />
        <xsl:apply-templates select="following-sibling::section[1][@paragraphMarker='false']" mode="text" />
    </xsl:template>
</xsl:stylesheet>

Since this code only looks forward, it is much more efficient than a solution doing backtracking (the XPath axes which are going backwards are slow in many implementations).


This transformation:

<xsl:stylesheet version="1.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
 <xsl:output omit-xml-declaration="yes" indent="yes"/>
 <xsl:strip-space elements="*"/>

 <xsl:key name="kSectsByPara"
  match="section[not(@paragraphMarker='true')]"
  use="generate-id(preceding-sibling::*
                    [@paragraphMarker='true']
                    [1]
                   )"
 />

 <xsl:template match="*[@paragraphMarker='true']">
  <p>
   <xsl:copy-of select=
    "text()|key('kSectsByPara', generate-id())/text()"/>
  </p>
 </xsl:template>

 <xsl:template match="*/*[not(@paragraphMarker='true')]"/>
</xsl:stylesheet>

when applied on the provided XML document:

<xmldoc>
    <section paragraphMarker="true">Part 1. </section>
    <section paragraphMarker="false">Part 2. </section>
    <section paragraphMarker="false">Part 3. </section>
    <section paragraphMarker="true">Part 4. </section>
    <section paragraphMarker="true">Part 5. </section>
    <section paragraphMarker="false">Part 6. </section>
</xmldoc>

produces the wanted, correct result:

<p>Part 1. Part 2. Part 3. </p>
<p>Part 4. </p>
<p>Part 5. Part 6. </p>

Explanation:

The <xsl:key> named "kSectsByPara" defines the mapping between a generate-id() of a section having attribute paragraphMarker="true" and the group of following section elements that have their attribute paragraphMarker="false" .


The following stylesheet:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:template match="section[@paragraphMarker='true']">
        <p>
            <xsl:apply-templates
               select=".|following-sibling::section[not(@paragraphMarker='true')]
           [preceding-sibling::section[@paragraphMarker='true'][1]=current()]" 
               mode="inner"/>
        </p>
    </xsl:template>
    <xsl:template match="section" />
</xsl:stylesheet>

Produces the desired output:

<p>Part 1. Part 2. Part 3. </p>
<p>Part 4. </p>
<p>Part 5. Part 6. </p>

In English, we're selecting all of the following siblings that are not a paragraph marker and whose first preceding sibling that is a paragraph marker is the current marker. Note that this requires us to move through all of the following siblings and then backtrack to look for the previous marker (in each case).

0

精彩评论

暂无评论...
验证码 换一张
取 消