How to get nodes grouped in XSLT 1.0 - grouping

I'm trying to group some nodes in XSLT 1.0 but can't manage to get the correct result...
Anyone an idea on how I could get the different <book> nodes with name HP_BINNENWERK grouped like the desire result is showing?
And is there a way to sort the <page> by numbers? So from the 003 to 069?
BASE XML:
<?xml version="1.0" encoding="UTF-8"?>
<root>
<PartProducts>
<PartProduct name="DAZ" code="C" pubCode="DAZ" pubDate="2021-03-16" modifiedDate="2021-03-12T19:50:53.7" zone="GP">
<books>
<book code="V" pageWidth="207" pageHeight="270" folios="1-2">
<name>CV_Cover01</name>
<Pages>
<Page id="001DAZCVGP21031600101" name="DPP_001_GPVXQU_20210316_DAZCV_00" nr="1" fullPageAd="false" zone="GP">
<PublishedAds/>
</Page>
<Page id="002DAZCVGP21031600102" name="DPP_002_GPVXQU_20210316_DAZCV_00" nr="2" fullPageAd="true" zone="GP">
<PublishedAds>
<Ad id="208050-541647-817428" x="0.0" y="0.0" width="207" height="270" bleed="true" cols="6" system="DPPPROD" materialPath="adv_dpp_ads/Adbase/Ad/72/100208872-01.pdf" uaid="0000208872-01"/>
</PublishedAds>
</Page>
</Pages>
</book>
<book code="V" pageWidth="207" pageHeight="270" folios="179-180">
<name>CV_Cover02</name>
<Pages>
<Page id="179DAZCVGP21031600103" name="DPP_179_GPVXQU_20210316_DAZCV_00" nr="179" fullPageAd="true" zone="GP">
<PublishedAds>
<Ad id="207381-539404-814747" x="0.0" y="0.0" width="207" height="270" bleed="true" cols="6" system="DPPPROD" materialPath="adv_dpp_ads/Adbase/Ad/01/100208201-08.pdf" uaid="0000208201-08"/>
</PublishedAds>
</Page>
<Page id="180DAZCVGP21031600104" name="DPP_180_GPVXQU_20210316_DAZCV_00" nr="180" fullPageAd="true" zone="GP">
<PublishedAds>
<Ad id="207218-539011-814313" x="0.0" y="0.0" width="207" height="270" bleed="true" cols="6" system="DPPPROD" materialPath="adv_dpp_ads/Adbase/Ad/38/100208038-06.pdf" uaid="0000208038-06"/>
</PublishedAds>
</Page>
</Pages>
</book>
</books>
</PartProduct>
<PartProduct name="DAZ" code="H" pubCode="DAZ" pubDate="2021-03-16" modifiedDate="2021-03-12T19:50:53.7" zone="GP">
<books>
<book code="P" pageWidth="207" pageHeight="270" folios="3-34,147-178">
<name>HP_BINNENWERK</name>
<Pages>
<Page id="003DAZHPGP21031600201" name="DPP_003_GPVXQU_20210316_DAZHP_00" nr="3" fullPageAd="false" zone="GP">
<PublishedAds/>
</Page>
<Page id="004DAZHPGP21031600202" name="DPP_004_GPVXQU_20210316_DAZHP_00" nr="4" fullPageAd="false" zone="GP">
<PublishedAds/>
</Page>
<Page id="015DAZHPGP21031600213" name="DPP_015_GPVXQU_20210316_DAZHP_00" nr="15" fullPageAd="true" zone="GP">
<PublishedAds>
<Ad id="207854-540913-816617" x="0.0" y="0.0" width="207" height="270" bleed="true" cols="6" system="DPPPROD" materialPath="adv_dpp_ads/Adbase/Ad/74/100208674-01.pdf" uaid="0000208674-01"/>
</PublishedAds>
</Page>
<Page id="016DAZHPGP21031600214" name="DPP_016_GPVXQU_20210316_DAZHP_00" nr="16" fullPageAd="false" zone="GP">
<PublishedAds/>
</Page>
<Page id="017DAZHPGP21031600215" name="DPP_017_GPVXQU_20210316_DAZHP_00" nr="17" fullPageAd="false" zone="GP">
<PublishedAds/>
</Page>
</Pages>
</book>
</books>
</PartProduct>
<PartProduct name="DAZ" code="H" pubCode="DAZ" pubDate="2021-03-16" modifiedDate="2021-03-12T19:50:53.7" zone="GP">
<books>
<book code="P" pageWidth="207" pageHeight="270" folios="35-66,115-146">
<name>HP_BINNENWERK</name>
<Pages>
<Page id="035DAZHPGP21031600301" name="DPP_035_GPVXQU_20210316_DAZHP_00" nr="35" fullPageAd="false" zone="GP">
<PublishedAds/>
</Page>
<Page id="036DAZHPGP21031600302" name="DPP_036_GPVXQU_20210316_DAZHP_00" nr="36" fullPageAd="true" zone="GP">
<PublishedAds>
<Ad id="207846-541774-817568" x="0.0" y="0.0" width="207" height="270" bleed="true" cols="6" system="DPPPROD" materialPath="adv_dpp_ads/Adbase/Ad/66/100208666-02.pdf" uaid="0000208666-02"/>
</PublishedAds>
</Page>
</Pages>
</book>
</books>
</PartProduct>
<PartProduct name="DAZ" code="H" pubCode="DAZ" pubDate="2021-03-16" modifiedDate="2021-03-12T19:50:53.7" zone="GP">
<books>
<book code="P" pageWidth="207" pageHeight="270" folios="67-90,91-114">
<name>HP_BINNENWERK</name>
<Pages>
<Page id="067DAZHPGP21031600401" name="DPP_067_GPVXQU_20210316_DAZHP_00" nr="67" fullPageAd="false" zone="GP">
<PublishedAds/>
</Page>
<Page id="068DAZHPGP21031600402" name="DPP_068_GPVXQU_20210316_DAZHP_00" nr="68" fullPageAd="false" zone="GP">
<PublishedAds/>
</Page>
<Page id="069DAZHPGP21031600403" name="DPP_069_GPVXQU_20210316_DAZHP_00" nr="69" fullPageAd="false" zone="GP">
<PublishedAds/>
</Page>
</Pages>
</book>
</books>
</PartProduct>
</PartProducts>
<UnplacedAds>
<Ad id="208165-541998-817817" width="16" height="4" bleed="false" cols="1" system="DPPPROD" uaid="0000208987-01"/>
</UnplacedAds>
</root>
DESIRE RESULT:
<?xml version="1.0" encoding="UTF-8"?>
<root>
<PartProducts>
<PartProduct name="DAZ" code="C" pubCode="DAZ" pubDate="2021-03-16" modifiedDate="2021-03-12T19:50:53.7" zone="GP">
<books>
<book code="V" pageWidth="207" pageHeight="270" folios="1-2">
<name>CV_Cover01</name>
<Pages>
<Page id="001DAZCVGP21031600101" name="DPP_001_GPVXQU_20210316_DAZCV_00" nr="1" fullPageAd="false" zone="GP">
<PublishedAds/>
</Page>
<Page id="002DAZCVGP21031600102" name="DPP_002_GPVXQU_20210316_DAZCV_00" nr="2" fullPageAd="true" zone="GP">
<PublishedAds>
<Ad id="208050-541647-817428" x="0.0" y="0.0" width="207" height="270" bleed="true" cols="6" system="DPPPROD" materialPath="adv_dpp_ads/Adbase/Ad/72/100208872-01.pdf" uaid="0000208872-01"/>
</PublishedAds>
</Page>
</Pages>
</book>
<book code="V" pageWidth="207" pageHeight="270" folios="179-180">
<name>CV_Cover02</name>
<Pages>
<Page id="179DAZCVGP21031600103" name="DPP_179_GPVXQU_20210316_DAZCV_00" nr="179" fullPageAd="true" zone="GP">
<PublishedAds>
<Ad id="207381-539404-814747" x="0.0" y="0.0" width="207" height="270" bleed="true" cols="6" system="DPPPROD" materialPath="adv_dpp_ads/Adbase/Ad/01/100208201-08.pdf" uaid="0000208201-08"/>
</PublishedAds>
</Page>
<Page id="180DAZCVGP21031600104" name="DPP_180_GPVXQU_20210316_DAZCV_00" nr="180" fullPageAd="true" zone="GP">
<PublishedAds>
<Ad id="207218-539011-814313" x="0.0" y="0.0" width="207" height="270" bleed="true" cols="6" system="DPPPROD" materialPath="adv_dpp_ads/Adbase/Ad/38/100208038-06.pdf" uaid="0000208038-06"/>
</PublishedAds>
</Page>
</Pages>
</book>
</books>
</PartProduct>
<PartProduct name="DAZ" code="H" pubCode="DAZ" pubDate="2021-03-16" modifiedDate="2021-03-12T19:50:53.7" zone="GP">
<books>
<book code="P" pageWidth="207" pageHeight="270">
<name>HP_BINNENWERK</name>
<Pages>
<Page id="003DAZHPGP21031600201" name="DPP_003_GPVXQU_20210316_DAZHP_00" nr="3" fullPageAd="false" zone="GP">
<PublishedAds/>
</Page>
<Page id="004DAZHPGP21031600202" name="DPP_004_GPVXQU_20210316_DAZHP_00" nr="4" fullPageAd="false" zone="GP">
<PublishedAds/>
</Page>
<Page id="015DAZHPGP21031600213" name="DPP_015_GPVXQU_20210316_DAZHP_00" nr="15" fullPageAd="true" zone="GP">
<PublishedAds>
<Ad id="207854-540913-816617" x="0.0" y="0.0" width="207" height="270" bleed="true" cols="6" system="DPPPROD" materialPath="adv_dpp_ads/Adbase/Ad/74/100208674-01.pdf" uaid="0000208674-01"/>
</PublishedAds>
</Page>
<Page id="016DAZHPGP21031600214" name="DPP_016_GPVXQU_20210316_DAZHP_00" nr="16" fullPageAd="false" zone="GP">
<PublishedAds/>
</Page>
<Page id="017DAZHPGP21031600215" name="DPP_017_GPVXQU_20210316_DAZHP_00" nr="17" fullPageAd="false" zone="GP">
<PublishedAds/>
</Page>
<Page id="035DAZHPGP21031600301" name="DPP_035_GPVXQU_20210316_DAZHP_00" nr="35" fullPageAd="false" zone="GP">
<PublishedAds/>
</Page>
<Page id="036DAZHPGP21031600302" name="DPP_036_GPVXQU_20210316_DAZHP_00" nr="36" fullPageAd="true" zone="GP">
<PublishedAds>
<Ad id="207846-541774-817568" x="0.0" y="0.0" width="207" height="270" bleed="true" cols="6" system="DPPPROD" materialPath="adv_dpp_ads/Adbase/Ad/66/100208666-02.pdf" uaid="0000208666-02"/>
</PublishedAds>
</Page>
<Page id="067DAZHPGP21031600401" name="DPP_067_GPVXQU_20210316_DAZHP_00" nr="67" fullPageAd="false" zone="GP">
<PublishedAds/>
</Page>
<Page id="068DAZHPGP21031600402" name="DPP_068_GPVXQU_20210316_DAZHP_00" nr="68" fullPageAd="false" zone="GP">
<PublishedAds/>
</Page>
<Page id="069DAZHPGP21031600403" name="DPP_069_GPVXQU_20210316_DAZHP_00" nr="69" fullPageAd="false" zone="GP">
<PublishedAds/>
</Page>
</Pages>
</book>
</books>
</PartProduct>
</PartProducts>
<UnplacedAds>
<Ad id="208165-541998-817817" width="16" height="4" bleed="false" cols="1" system="DPPPROD" uaid="0000208987-01"/>
</UnplacedAds>
</root>
EDIT
With this XSLT, I can get the result I wanted (thanks to #michael.hor257k) but I can't sort the <page>... It worked in a previous stadium of my XSLT but it does not anymore...
Any idea why?
XSLT:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
exclude-result-prefixes="xs"
version="1.0">
<xsl:output method="xml" encoding="UTF-8" indent="yes"/>
<xsl:key name="PartProduct-by-name" match="PartProduct" use="books/book/name"/>
<xsl:key name="Book-by-name" match="book" use="name"/>
<xsl:template match="PartProducts">
<PartProducts>
<xsl:for-each select="PartProduct[count(.|key('PartProduct-by-name', books/book/name)[1]) = 1]">
<PartProduct>
<xsl:copy-of select="#*"/>
<books>
<xsl:for-each select="books/book[count(.|key('Book-by-name', name)[1]) = 1]">
<book>
<xsl:copy-of select="#*[name()!='folios']"/>
<name><xsl:value-of select="name"/></name>
<Pages>
<xsl:for-each select="key('Book-by-name', name)">
<xsl:copy-of select="Pages/Page"/>
</xsl:for-each>
</Pages>
</book>
</xsl:for-each>
</books>
</PartProduct>
</xsl:for-each>
</PartProducts>
</xsl:template>
<xsl:template match="Pages">
<xsl:copy>
<xsl:apply-templates select="Page">
<xsl:sort select="#id" order="descending"/>
</xsl:apply-templates>
</xsl:copy>
</xsl:template>
<xsl:template match="#* | node()">
<xsl:copy>
<xsl:apply-templates select="#* | node()"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>

Related

XSLT Transformation connect nodes with same names with parent node

I want to transform the following XML-data to the desired output shown below - without success until now. So I hope of an input from an expert relating the xsl. Thank you in advance.
Input XML-Data
<?xml version="1.0" encoding="utf-8"?>
<pages>
<page number="1">
<pageArticles>
<articleID id="100"/>
<articleID id="200"/>
<articleID id="300"/>
</pageArticles>
</page>
<page number="5">
<pageArticles/>
</page>
<page number="9">
<pageArticles>
<articleID id="400"/>
<articleID id="500"/>
</pageArticles>
</page>
</pages>
Desired Ouput
<?xml version="1.0" encoding="utf-8"?>
<pages>
<page>
<number>1</number>
<articleID>100</articleID>
</page>
<page>
<number>1</number>
<articleID>200</articleID>
</page>
<page>
<number>1</number>
<articleID>300</articleID>
</page>
<page>
<number>5</number>
<articleID></articleID>
</page>
<page>
<number>9</number>
<articleID>400</articleID>
</page>
<page>
<number>9</number>
<articleID>500</articleID>
</page>
</pages>
Many thanks in advance for any assistance!
Urs
This XSLT-1.0 stylesheet does the job:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" omit-xml-declaration="yes" indent="yes"/>
<xsl:template match="text()" />
<xsl:template match="/pages">
<xsl:copy>
<xsl:apply-templates />
</xsl:copy>
</xsl:template>
<xsl:template match="//articleID | pageArticles[not(*)]">
<page>
<number><xsl:value-of select="ancestor::page/#number[1]" /></number>
<articleID><xsl:value-of select="#id" /></articleID>
</page>
</xsl:template>
</xsl:stylesheet>
Output is:
<pages>
<page>
<number>1</number>
<articleID>100</articleID>
</page>
<page>
<number>1</number>
<articleID>200</articleID>
</page>
<page>
<number>1</number>
<articleID>300</articleID>
</page>
<page>
<number>5</number>
<articleID/>
</page>
<page>
<number>9</number>
<articleID>400</articleID>
</page>
<page>
<number>9</number>
<articleID>500</articleID>
</page>
</pages>
Write a template for those articleID elements to collect the number from the grandparent and add a template for empty pageArticles:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
expand-text="yes"
version="3.0">
<xsl:output indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="pages">
<xsl:copy>
<xsl:apply-templates select="page/pageArticles"/>
</xsl:copy>
</xsl:template>
<xsl:template match="pageArticles[not(has-children())]">
<page>
<number>{../#number}</number>
<articleID/>
</page>
</xsl:template>
<xsl:template match="articleID">
<page>
<number>{../../#number}</number>
<articleID>{#id}</articleID>
</page>
</xsl:template>
</xsl:stylesheet>
https://xsltfiddle.liberty-development.net/bFDb2BT and the above is XSLT 3. In earlier version of XSLT you have to replace the text value templates (e.g. {../#number}) with xsl:value-of (e.g. <xsl:value-of select="../#number"/>) and the predicate [not(has-children())] with [not(node()].

demerge the values between merged cells

Can someone help me with the below xslt issue.
I have table with 5 rows.
First 3 cells(1,2,3) are merged and first cell has value.
Next 2 cells(4,5) are merged and fourth cell has value.
In this case my xslt is working fine for the sample input.ie) if the cells are merged and its first cell has value then the xslt is working.
But incase if fourth cell dont have any value then xslt is not working fine.
how to find how many cells are merged together?
If rowspan =3 then three cells are merged. Also if rowspan=0 then this cell is merged with previous row.
If rowapsn=1 then cell is not merged.
Also if rowmerged='T' then the cell is merged and rowmerged='F' means cell is not merged
Sample input:
<catalog>
<cd>
<title rowmerge="F">Title 1</title>
<artist rowmerge="F">sample 1</artist>
<price rowmerge="T" rowspan="3" >1</price>
<year >1985</year>
</cd>
<cd>
<title rowmerge="F">Title 1</title>
<artist rowmerge="F">sample 1</artist>
<price rowmerge="T" rowspan="0"></price>
<year >1985</year>
</cd>
<cd>
<title rowmerge="F">Title 3</title>
<artist rowmerge="F">Sample 3</artist>
<price rowmerge="T" rowspan="0"></price>
<year>1988</year>
</cd>
<cd>
<title rowmerge="T">Title 4</title>
<artist rowmerge="F">sample 4</artist>
<price rowmerge="T" rowspan="2"></price>
<year >1985</year>
</cd>
<cd>
<title rowmerge="T"></title>
<artist rowmerge="F">Sample 5</artist>
<price rowmerge="T" rowspan="0"></price>
<year>1988</year>
</cd>
</catalog>
Expected output:
<?xml version="1.0"?>
<catalog>
<cd>
<title rowmerge="f">Title 1</title>
<artist rowmerge="f">sample 1</artist>
<price rowmerge="F" rowspan="3">1</price>
<year>1985</year>
</cd>
<cd>
<title rowmerge="f">Title 1</title>
<artist rowmerge="f">sample 1</artist>
<price rowmerge="F" rowspan="0">1</price>
<year>1985</year>
</cd>
<cd>
<title rowmerge="F">Title 3</title>
<artist rowmerge="F">Sample 3</artist>
<price rowmerge="F" rowspan="0">1</price>
<year>1988</year>
</cd>
<cd>
<title rowmerge="F">Title 4</title>
<artist rowmerge="F">sample 4</artist>
<price rowmerge="F" rowspan="2"/>
<year>1985</year>
</cd>
<cd>
<title rowmerge="F"/>
<artist rowmerge="F">Sample 5</artist>
<price rowmerge="F" rowspan="0"/>
<year>1988</year>
</cd>
</catalog>
tried with below xslt:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output method="xml" indent="yes" />
<xsl:template match="*[#rowmerge='T'][not(normalize-space())] [#rowspan= 0] ">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
<xsl:value-of select="(../preceding-sibling::*/*[name() = name(current())][normalize-space()])[last()]/node()" />
</xsl:copy>
</xsl:template>
<xsl:template match="#rowmerge[. = 'T']">
<xsl:attribute name="rowmerge">F</xsl:attribute>
</xsl:template>
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
What i am getting:
<?xml version="1.0"?>
<catalog>
<cd>
<title rowmerge="f">Title 1</title>
<artist rowmerge="f">sample 1</artist>
<price rowmerge="F" rowspan="3">1</price>
<year>1985</year>
</cd>
<cd>
<title rowmerge="f">Title 1</title>
<artist rowmerge="f">sample 1</artist>
<price rowmerge="F" rowspan="0">1</price>
<year>1985</year>
</cd>
<cd>
<title rowmerge="F">Title 3</title>
<artist rowmerge="F">Sample 3</artist>
<price rowmerge="F" rowspan="0">1</price>
<year>1988</year>
</cd>
<cd>
<title rowmerge="F">Title 4</title>
<artist rowmerge="F">sample 4</artist>
<price rowmerge="F" rowspan="2"/>
<year>1985</year>
</cd>
<cd>
<title rowmerge="F"/>
<artist rowmerge="F">Sample 5</artist>
<price rowmerge="F" rowspan="0">1</price>
<year>1988</year>
</cd>
</catalog>
You can achieve this with a small change to one of your predicates:
<xsl:template match="*[#rowmerge='T'][not(normalize-space())] [#rowspan= 0] ">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
<xsl:value-of select="(../preceding-sibling::*/*[name() = name(current())]
[#rowspan != 0])[last()]" />
</xsl:copy>
</xsl:template>
I've changed the [normalize-space()] to [#rowspan != 0] - the "head" cell for this span is not necessarily the nearest preceding one with a non-empty value, but it will always be the nearest preceding one with a non-zero rowspan.
(I've also removed the redundant /node() from the end of the value-of).

Demerge the merged cell in XSLT

Can someone help me with the below xslt question.
<?xml version="1.0" encoding="UTF-8"?>
<catalog>
<cd>
<title rowmerge="f">Title 1</title>
<artist rowmerge="f">sample 1</artist>
<price rowmerge="T">1</price>
<year >1985</year>
</cd>
<cd>
<title rowmerge="F">Title 2</title>
<artist rowmerge="F">Sample 2</artist>
<price rowmerge="T"></price>
<year>1988</year>
</cd>
<cd>
<title rowmerge="F">Title 3</title>
<artist rowmerge="F">Sample 3</artist>
<price rowmerge="F">3</price>
<year>1988</year>
</cd>
<cd>
<title rowmerge="T">Title 4</title>
<artist rowmerge="F">sample 4</artist>
<price rowmerge="T">4</price>
<year >1985</year>
</cd>
<cd>
<title rowmerge="T"></title>
<artist rowmerge="F">Sample 5</artist>
<price rowmerge="T"></price>
<year>1988</year>
</cd>
</catalog>
Expected output:
<?xml version="1.0" encoding="UTF-8"?>
<catalog>
<cd>
<title rowmerge="f">Title 1</title>
<artist rowmerge="f">sample 1</artist>
<price rowmerge="f">1</price>
<year >1985</year>
</cd>
<cd>
<title rowmerge="F">Title 2</title>
<artist rowmerge="F">Sample 2</artist>
<price rowmerge="f">1</price>
<year>1988</year>
</cd>
<cd>
<title rowmerge="F">Title 3</title>
<artist rowmerge="F">Sample 3</artist>
<price rowmerge="F">3</price>
<year>1988</year>
</cd>
<cd>
<title rowmerge="F">Title 4</title>
<artist rowmerge="F">sample 4</artist>
<price rowmerge="F">4</price>
<year >1985</year>
</cd>
<cd>
<title rowmerge="F">Title 4</title>
<artist rowmerge="F">Sample 5</artist>
<price rowmerge="F">4</price>
<year>1988</year>
</cd>
</catalog>
If rowmerge attribute is 'T' in the first cd for any tag (title/artist/price)then I need to copy the price value from first cd to next cd. I am new to xslt.
You first should read up on the XSLT Identity Template, which on its own will copy nodes from the source document to the output.
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
What this means is that you only need to write templates for the nodes you wish to transform. Considering just the price elements for now, you are trying to amend price elements which have a rowmerge set to "T" and which are empty. In this case, you want to copy the price from the first most preceding 'cd'. This is achieved like so:
<xsl:template match="price[#rowmerge='T'][not(normalize-space())]">
<price>
<xsl:apply-templates select="#*|node()"/>
<xsl:value-of select="../preceding-sibling::*[1]/price" />
</price>
</xsl:template>
So, it is very similar to the identity template, but it has the extra xsl:value-of statement to copy the value from the preceding node. Or rather the price value from the preceding node of the parent cd element.
Of course, you could repeat this template for each of the possible child elements of cd, but this would be a lot of repetitive coding. Better would be to have a more generic template to cover all cases:
<xsl:template match="*[#rowmerge='T'][not(normalize-space())]">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
<xsl:value-of select="../preceding-sibling::*[1]/*[name() = name(current())]" />
</xsl:copy>
</xsl:template>
Try this XSLT
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output method="xml" indent="yes" />
<xsl:template match="*[#rowmerge='T'][not(normalize-space())]">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
<xsl:value-of select="../preceding-sibling::*[1]/*[name() = name(current())]" />
</xsl:copy>
</xsl:template>
<xsl:template match="#rowmerge[. = 'T']">
<xsl:attribute name="rowmerge">F</xsl:attribute>
</xsl:template>
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
Do note there is also a template to convert the rowmerge attributes from having a value of T to F.
EDIT: In answer to your comment, if you have to look more than one sibling back (that is to say, you have two consecutive elements that are empty), then try one of these two expressions
<xsl:value-of select="../preceding-sibling::*[*[name() = name(current())][normalize-space()]][1]/*[name() = name(current())]" />
<xsl:value-of select="(../preceding-sibling::*/*[name() = name(current())][normalize-space()])[last()]" />
EDIT 2: If the nodes contain more than just text, then to copy all the child elements, you use xsl:copy-of instead of xsl:value-of. For example...
<xsl:copy-of select="../preceding-sibling::*[1]/*[name() = name(current())]/node()" />
Note the use of node() on the end, to ensure only the child nodes are copied, not the price (for example) element itself.

How to Strip out specific Node using XSLT

I have the following xml and looking to produce an out put which contains only GENRE_1 and GENRE_3 and any other book ids. This means GENRE_4, 5 and 6 will be stripped out. I have tried using the sample xslt but not getting it right. Will appreciate any help.
<bookstore xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<Payload xmlns:ns0="http://www.themindelectric.com/">
<books xmlns:ns0="http://www.themindelectric.com/collections/">
<book xmlns:ns0="http://www.themindelectric.com">
<id>GENRE_1</id>
<title lang="en">Everyday Italian</title>
<author>Giada De Laurentiis</author>
<year>2001</year>
<price>30.00</price>
</book>
<book xmlns:ns0="http://www.themindelectric.com">
<id>GENRE_3</id>
<title lang="en">Harry Potter</title>
<author>J K. Rowling</author>
<year>2003</year>
<price>29.99</price>
</book>
<book xmlns:ns0="http://www.themindelectric.com">
<id>TEST_3</id>
<title lang="en">Harry Potter</title>
<author>J K. Rowling</author>
<year>2003</year>
<price>29.99</price>
</book>
<book xmlns:ns0="http://www.themindelectric.com">
<id>ANOTHER_1</id>
<title lang="en">Harry Potter</title>
<author>J K. Rowling</author>
<year>2003</year>
<price>29.99</price>
</book>
<book xmlns:ns0="http://www.themindelectric.com">
<id>GENRE_5</id>
<title lang="en">XQuery Kick Start</title>
<author>James McGovern</author>
<author>Per Bothner</author>
<author>Kurt Cagle</author>
<author>James Linn</author>
<author>Vaidyanathan Nagarajan</author>
<year>2003</year>
<price>49.99</price>
</book>
<book xmlns:ns0="http://www.themindelectric.com">
<id>GENRE_1</id>
<title lang="en">Learning XML</title>
<author>Erik T. Ray</author>
<year>2005</year>
<price>39.95</price>
</book>
<book xmlns:ns0="http://www.themindelectric.com">
<id>GENRE_1</id>
<title lang="en">XQuery Kick Start</title>
<author>James McGovern</author>
<author>Per Bothner</author>
<author>Kurt Cagle</author>
<author>James Linn</author>
<author>Vaidyanathan Nagarajan</author>
<year>2007</year>
<price>49.99</price>
</book>
<book xmlns:ns0="http://www.themindelectric.com">
<id>GENRE_6</id>
<title lang="en">Learning Java</title>
<author>Testing</author>
<year>2005</year>
<price>39.95</price>
</book>
<book xmlns:ns0="http://www.themindelectric.com">
<id>GENRE_4</id>
<title lang="en">XQuery Kick Start</title>
<author>James McGovern</author>
<author>Per Bothner</author>
<author>Kurt Cagle</author>
<author>James Linn</author>
<author>Vaidyanathan Nagarajan</author>
<year>2007</year>
<price>49.99</price>
</book>
</books>
</Payload>
EXPECTED OUTPUT
<bookstore xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<Payload xmlns:ns0="http://www.themindelectric.com/">
<books xmlns:ns0="http://www.themindelectric.com/collections/">
<book xmlns:ns0="http://www.themindelectric.com">
<id>GENRE_1</id>
<title lang="en">Everyday Italian</title>
<author>Giada De Laurentiis</author>
<year>2001</year>
<price>30.00</price>
</book>
<book xmlns:ns0="http://www.themindelectric.com">
<id>GENRE_3</id>
<title lang="en">Harry Potter</title>
<author>J K. Rowling</author>
<year>2003</year>
<price>29.99</price>
</book>
<book xmlns:ns0="http://www.themindelectric.com">
<id>TEST_3</id>
<title lang="en">Harry Potter</title>
<author>J K. Rowling</author>
<year>2003</year>
<price>29.99</price>
</book>
<book xmlns:ns0="http://www.themindelectric.com">
<id>ANOTHER_1</id>
<title lang="en">Harry Potter</title>
<author>J K. Rowling</author>
<year>2003</year>
<price>29.99</price>
</book>
<book xmlns:ns0="http://www.themindelectric.com">
<id>GENRE_1</id>
<title lang="en">Learning XML</title>
<author>Erik T. Ray</author>
<year>2005</year>
<price>39.95</price>
</book>
<book xmlns:ns0="http://www.themindelectric.com">
<id>GENRE_1</id>
<title lang="en">XQuery Kick Start</title>
<author>James McGovern</author>
<author>Per Bothner</author>
<author>Kurt Cagle</author>
<author>James Linn</author>
<author>Vaidyanathan Nagarajan</author>
<year>2007</year>
<price>49.99</price>
</book>
</books>
</Payload>
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:ns0="http://www.themindelectric.com">
<xsl:template match="node() | #*">
<xsl:copy>
<xsl:apply-templates select="node() | #*" />
</xsl:copy>
</xsl:template>
<xsl:template match="/bookstore/Payload/books/book[starts-with(id,'GENRE')]">
<xsl:call-template name="genre"/>
</xsl:template>
<xsl:template name="genre">
<xsl:choose>
<xsl:when
test="count(/bookstore/Payload/books/book[id='GENRE_1']) != 0 or
count(/bookstore/Payload/books/book[id='GENRE_3']) != 0">
<xsl:copy>
<xsl:apply-templates select="node() | #*" />
</xsl:copy>
</xsl:when>
<xsl:otherwise />
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
If I understand correctly your new(!) requirement, you want to exclude any book that has an id that starts with GENRE, except GENRE_1 and GENRE_3:
XSLT 1.0
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:strip-space elements="*"/>
<!-- identity transform -->
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="book[starts-with(id, 'GENRE') and not(id='GENRE_1' or id='GENRE_3')]"/>
</xsl:stylesheet>
A simple solution is just to have an empty template matching all books not having the genre id that you want to keep:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:ns0="http://www.themindelectric.com">
<xsl:template match="node() | #*">
<xsl:copy>
<xsl:apply-templates select="node() | #*" />
</xsl:copy>
</xsl:template>
<xsl:template match="book[not(id='GENRE_1' or id='GENRE_3'
or id='TEST_3' or id='ANOTHER_1')]"/>
</xsl:stylesheet>
This copies everything except books that have not the ids you want to keep.
Update: For the clarified requirement in the updated question: to keep all books that either have the id GENRE_1 or GENRE_3 or have an id not matching GENRE:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:ns0="http://www.themindelectric.com">
<xsl:template match="node() | #*">
<xsl:copy>
<xsl:apply-templates select="node() | #*" />
</xsl:copy>
</xsl:template>
<xsl:template match="book[starts-with(id, 'GENRE_') and
not(contains(id,1) or contains(id,3))]"/>
</xsl:stylesheet>
As mentioned in the comments by michael.hor257k, using contains() in the match pattern won't work in case you have to handle more than 9 genres. In this case just matching the ids you want to keep in the not() - not(id='GENRE_1' or id='GENRE_3') - like in the correct answer given by michael.hor257k is the right approach.

How to do xslt Muenchian grouping with some null attributes?

Slightly different to the standard Meunchain grouping as I'm dealing with a null(?) attribute for some of the tags that I'm transforming. I'd like the null groupings to be treated as their own individual group with the transformed output adding the grouped strings. Also if there is a grouping to do a count of how many there were.
<root>
<section>
<subsection>
<module>
<comp>111</comp>
</module>
<module group='group01'>
<comp>222</comp>
</module>
<module group='group01'>
<comp>333</comp>
</module>
<module>
<comp>444</comp>
</module>
<module>
<comp>555</comp>
</module>
</subsection>
</section>
<section>
<subsection>
<module group ="group02">
<comp>666</comp>
</module>
<module group ="group02">
<comp>777</comp>
</module>
<module>
<comp>888</comp>
</module>
<module group ="group03">
<comp>999</comp>
</module>
<module group ="group03">
<comp>101010</comp>
</module>
</subsection>
<subsection>
<module group ="group04">
<comp>11111</comp>
</module>
<module group ="group04">
<comp>121212</comp>
</module>
<module group ="group05">
<comp>131313</comp>
</module>
<module group ="group05">
<comp>141414</comp>
</module>
<module group ="group06">
<comp>151515</comp>
</module>
<module group ="group06">
<comp>161616</comp>
</module>
<module>
<comp>171717</comp>
</module>
</subsection>
</section>
Wanted output:
<AllSections>
<section>
<subsection>
<page>
<content>111</content>
</page>
<page>
<content>222333</content>
<count>2</count>
</page>
<page>
<content>444</content>
</page>
<page>
<content>555</content>
</page>
</subsection>
</section>
<section>
<subsection>
<page>
<content>666777</content>
<count>2</count>
</page>
<page>
<content>888</content>
</page>
<page>
<content>999101010</content>
<count>2</count>
</page>
</subsection>
<subsection>
<page>
<content>111111121212</content>
<count>2</count>
</page>
<page>
<content>131313141414161616</content>
<count>3</count>
</page>
<page>
<content>151515</content>
</page>
<page>
<content>171717</content>
</page>
</subsection>
</section>
Thanks!
For the elements with a group attribute, you are grouping by that attribute but also within the parent subsection element. Therefore you could start off by defining a key to group them this was
<xsl:key name="modules" match="module[#group]" use="concat(generate-id(..), '|', #group)" />
Next, you would need templates to match the various cases for the module elements. Firstly, you could have a template to match module elements with no group attribute, where you could format the output as required.
<xsl:template match="module[not(#group)]">
<page>
<content>
<xsl:value-of select="comp"/>
</content>
</page>
</xsl:template>
For modules, with group attributes you would need a match that checked that this particular module element occurred first in the group for the key defined above.
<xsl:template
match="module
[#group]
[generate-id() = generate-id(key('modules', concat(generate-id(..), '|', #group))[1])]">
Within this template, you could then easily define a variable to hold the elements in the group, using the key, and then either output the child comp elements, or count them
<xsl:variable name="modules" select="key('modules', concat(generate-id(..), '|', #group))"/>
<page>
<content>
<xsl:apply-templates select="$modules/comp/text()"/>
</content>
<count>
<xsl:value-of select="count($modules)" />
</count>
</page>
Finally, you would need a third template to match all other module elements (i.e. elements with a group attribute, but not first in the group) to ignore them, to ensure they don't get output twice. (The XSLT processor should always match more specific templates before this more generic one)
<xsl:template match="module"/>
Here is the full XSLT
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes"/>
<xsl:key name="modules" match="module[#group]" use="concat(generate-id(..), '|', #group)"/>
<xsl:template match="root">
<AllSections>
<xsl:apply-templates />
</AllSections>
</xsl:template>
<xsl:template match="module[not(#group)]">
<page>
<content>
<xsl:value-of select="comp"/>
</content>
</page>
</xsl:template>
<xsl:template match="module[#group][generate-id() = generate-id(key('modules', concat(generate-id(..), '|', #group))[1])]">
<xsl:variable name="modules" select="key('modules', concat(generate-id(..), '|', #group))"/>
<page>
<content>
<xsl:apply-templates select="$modules/comp/text()"/>
</content>
<count>
<xsl:value-of select="count($modules)" />
</count>
</page>
</xsl:template>
<xsl:template match="module"/>
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
When applied to your sample XML, the following is output
<AllSections>
<section>
<subsection>
<page>
<content>111</content>
</page>
<page>
<content>222333</content>
<count>2</count>
</page>
<page>
<content>444</content>
</page>
<page>
<content>555</content>
</page>
</subsection>
</section>
<section>
<subsection>
<page>
<content>666777</content>
<count>2</count>
</page>
<page>
<content>888</content>
</page>
<page>
<content>999101010</content>
<count>2</count>
</page>
</subsection>
<subsection>
<page>
<content>11111121212</content>
<count>2</count>
</page>
<page>
<content>131313141414</content>
<count>2</count>
</page>
<page>
<content>151515161616</content>
<count>2</count>
</page>
<page>
<content>171717</content>
</page>
</subsection>
</section>
</AllSections>