Xsl - grouping nodes based on attributes between two nodes

In XSL 1.0, I had a search and you found similar elements around the grouping, but I think this is a little different. I apologize if this has already been considered, I could not find the answer

Enter

<?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 Result

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

I tried the following: -

 <xsl:key name="sectionsWithParagraphMarker" match="section[@paragraphMarker='true']" use="."/> <xsl:template match="/"> <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 does not work and I am stuck with it. He selects too many "sectional" nodes for all groups. Any help would be greatly appreciated!

+4
source share
3 answers
 <?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 is only looking forward, it is much more efficient than a solution that performs backtracking (XPath axes that go backward in many implementations are slow).

+4
source

This conversion is :

 <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 to 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> 

creates the desired, correct result :

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

Explanation

<xsl:key> with the name "kSectsByPara" defines the mapping between the generate-id() the section attribute with the paragraphMarker="true" attribute and the group of next section elements that have their own paragraphMarker="false" attribute.

+2
source

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 required output:

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

In English, we select all of the following siblings that are not a paragraph marker, and the first previous brother, which is a paragraph marker, is the current marker. Please note that this requires that we move all the following siblings and then back to look for the previous marker (in each case).

+1
source

Source: https://habr.com/ru/post/1347391/


All Articles