How to group elements based on their siblings using XSLT - xslt

I am trying to group several elements based on a starting and ending attribute of their surrounding siblings.
Sample XML:
<list>
<item>One</item>
<item class="start">Two</item>
<item>Three</item>
<item class="end">Four</item>
<item>Five</item>
<item class="start">Six</item>
<item class="end">Seven</item>
<item>Eight</item>
</list>
Desired Result:
<body>
<p>One</p>
<div>
<p>Two</p>
<p>Three</p>
<p>Four</p>
</div>
<p>Five</p>
<div>
<p>Six</p>
<p>Seven</p>
</div>
<p>Eight</p>
</body>
I come close to the desired results with the following XSLT. However, the following-sibling match doesn't stop after it reaches the ending attribute. Also, the standard matching repeats the elements that were already output from the following-sibling match.
<?xml version="1.0" encoding="UTF-8"?>
<xsl:transform version="2.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="*" />
<xsl:template match="/">
<xsl:apply-templates />
</xsl:template>
<xsl:template match="list">
<body>
<xsl:apply-templates />
</body>
</xsl:template>
<xsl:template match="item">
<p>
<xsl:apply-templates />
</p>
</xsl:template>
<xsl:template match="item[#class='start']">
<div>
<p><xsl:apply-templates /></p>
<xsl:apply-templates select="following-sibling::*[not(preceding-sibling::*[1][#class='end'])]" />
</div>
</xsl:template>
</xsl:transform>

Since you're using XSLT 2.0, why don't you take advantage of it:
<xsl:stylesheet version="2.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="*"/>
<xsl:template match="/list">
<body>
<xsl:for-each-group select="item" group-starting-with="item[#class='start']">
<xsl:for-each-group select="current-group()" group-ending-with="item[#class='end']">
<xsl:choose>
<xsl:when test="count(current-group()) gt 1">
<div>
<xsl:apply-templates select="current-group()" />
</div>
</xsl:when>
<xsl:otherwise>
<xsl:apply-templates select="current-group()" />
</xsl:otherwise>
</xsl:choose>
</xsl:for-each-group>
</xsl:for-each-group>
</body>
</xsl:template>
<xsl:template match="item">
<p>
<xsl:value-of select="."/>
</p>
</xsl:template>
</xsl:stylesheet>

Related

is shown by XSLT

I want print attribute and put some values to it.
Input :
<figure id="fig_1">
<dis>text</dis>
</figure>
my output:
<image ref="fig_1"
comment="text the
"/>
Tried code :
<xsl:template match="dis[parent::figure]">
<xsl:variable name="fig_name" select="parent::fig/#id"/>
<image ref="{$fig_name}">
<xsl:attribute name="comment">
<xsl:value-of select="text()"/>
</xsl:attribute>
</tps:image>
</xsl:template>
I want to remove all
. How can I do it.
use normalize-space() function to remove unnecessary white space.
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" method="xml" version="1.0" />
<xsl:template match="long-desc[parent::fig]">
<xsl:variable name="fig_name" select="parent::fig/#id"/>
<image ref="{$fig_name}">
<xsl:attribute name="comment">
<xsl:value-of select="normalize-space(text())"/>
</xsl:attribute>
</image>
</xsl:template>
</xsl:stylesheet>

How to resume xsl number count after nesting and while using level="any"

I am trying to have the <xsl:number/> resume its counter. However, since it is using level="any", it counts its previous sibling's child elements, which is undesirable.
Using the sample code below, everything looks correct except for the list items containing Three and Four. Those items are given the number counts of 5 and 6 because the counter is using the items nested within item Two instead of continuing from where item Two itself left off.
Sample Input XML:
<body>
<div class="list-wrapper">
<ol>
<li>One</li>
<li><span>Two</span>
<ol>
<li>AAA</li>
<li>BBB</li>
</ol>
<div class="list-wrapper">
<ol>
<li>CCC</li>
<li>DDD</li>
</ol>
<ol>
<li>EEE</li>
<li>FFF</li>
</ol>
</div>
<ol>
<li>GGG</li>
<li>HHH</li>
</ol>
</li>
</ol>
<ol>
<li>Three</li>
<li>Four</li>
</ol>
</div>
<div class="list-wrapper">
<ol>
<li>Five</li>
<li>Six</li>
</ol>
<ol>
<li>Seven</li>
<li>Eight</li>
</ol>
</div>
</body>
Sample Stylesheet:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:fo="http://www.w3.org/1999/XSL/Format">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()" />
</xsl:copy>
</xsl:template>
<xsl:template match="body">
<fo:root>
<xsl:apply-templates />
</fo:root>
</xsl:template>
<xsl:template match="div[#class='list-wrapper']">
<fo:list-block>
<xsl:apply-templates mode="list-wrapper" />
</fo:list-block>
</xsl:template>
<xsl:template match="ol" mode="list-wrapper">
<xsl:apply-templates mode="list-wrapper" />
</xsl:template>
<xsl:template match="li" mode="list-wrapper">
<fo:list-item>
<fo:list-item-label>
<xsl:number format="1." count="div[#class='list-wrapper']/ol/li" from="div[#class='list-wrapper']" level="any" />
</fo:list-item-label>
<fo:list-item-body>
<xsl:apply-templates />
</fo:list-item-body>
</fo:list-item>
</xsl:template>
<xsl:template match="ol">
<fo:list-block>
<xsl:apply-templates />
</fo:list-block>
</xsl:template>
<xsl:template match="li">
<fo:list-item>
<fo:list-item-label>
<xsl:number format="1."/>
</fo:list-item-label>
<fo:list-item-body>
<xsl:apply-templates />
</fo:list-item-body>
</fo:list-item>
</xsl:template>
</xsl:stylesheet>
Looks like I can get around the nesting problem by using an xsl:for-each to handle only the direct children of div[#class='list-wrapper'].
Now I can simply use position() to number each node as I require.
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:fo="http://www.w3.org/1999/XSL/Format">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()" />
</xsl:copy>
</xsl:template>
<xsl:template match="body">
<fo:root>
<xsl:apply-templates />
</fo:root>
</xsl:template>
<xsl:template match="div[#class='list-wrapper']">
<fo:list-block>
<xsl:for-each select="ol/li">
<xsl:call-template name="process-ordered-list" />
</xsl:for-each>
</fo:list-block>
</xsl:template>
<xsl:template match="li">
<xsl:call-template name="process-ordered-list" />
</xsl:template>
<xsl:template name="process-ordered-list">
<fo:list-item>
<fo:list-item-label>
<xsl:number format="1." value="position()" />
</fo:list-item-label>
<fo:list-item-body>
<xsl:apply-templates />
</fo:list-item-body>
</fo:list-item>
</xsl:template>
<xsl:template match="ol">
<fo:list-block>
<xsl:apply-templates />
</fo:list-block>
</xsl:template>
</xsl:stylesheet>
I was able to simplify the template by replacing the for-each with an apply-templates.
This solution appears best.
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:fo="http://www.w3.org/1999/XSL/Format">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="body">
<fo:root>
<xsl:apply-templates />
</fo:root>
</xsl:template>
<xsl:template match="div[#class='list-wrapper']">
<fo:list-block>
<xsl:apply-templates select="ol/li" />
</fo:list-block>
</xsl:template>
<xsl:template match="ol">
<fo:list-block>
<xsl:apply-templates />
</fo:list-block>
</xsl:template>
<xsl:template match="ol/li">
<fo:list-item>
<fo:list-item-label>
<xsl:number format="1." value="position()" />
</fo:list-item-label>
<fo:list-item-body>
<xsl:apply-templates />
</fo:list-item-body>
</fo:list-item>
</xsl:template>
</xsl:stylesheet>

xsl merge same elements

Following big problem:
<root>
<div>
<programm></programm>
<systemes><p></p></systemes>
<systemes><table>.1.</table></systemes>
<systemes><table>.2.</table></systemes>
<systemes><p></p></systemes>
<requirements></requirements>
</div>
<div>
<programm></programm>
<systemes><table>.1.</table></systemes>
<systemes><p></p></systemes>
<requirements></requirements>
</div>
<div>
<programm></programm>
<systemes><table>.1.</table></systemes>
<systemes><table>.2.</table></systemes>
<systemes><p></p></systemes>
<requirements></requirements>
</div>
</root>
I need the output to be this:
<root>
<div>
<programm></programm>
<systemes><p></p><table>.1.</table><table>.2.</table><p></p></systemes>
<requirements></requirements>
</div>
<div>
<programm></programm>
<systemes><table>.1.</table><p></p></systemes>
<requirements></requirements>
</div>
<div>
<programm></programm>
<systemes><table>.1.</table><table>.2.</table><p></p></systemes>
<requirements></requirements>
</div>
</root>
I hope someone can help me with this problem. I know the Muenchian Method but dont get it to work properly. Thank you very much!
This is what I tried so far:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes" omit-xml-declaration="yes" />
<xsl:strip-space elements="*"/>
<xsl:key name="systemsKey" match="//systemes" use="name()"/>
<xsl:template match="node()|#*">
<xsl:copy>
<xsl:apply-templates select="node()|#*" />
</xsl:copy>
</xsl:template>
<xsl:template match="systemes[generate-id()=generate-id(key('systemesKey', name())[1])]">
<xsl:copy>
<xsl:apply-templates select="#*|key('systemesKey', name())/node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="systemes[not(generate-id()=generate-id(key('systemesKey', name())[1]))]"/>
</xsl:stylesheet>
This produces exactly the output you described (except for the order of childs inside systemes elements).
<?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" indent="yes"/>
<xsl:template match="/">
<xsl:apply-templates/>
</xsl:template>
<xsl:template match="root">
<xsl:copy>
<xsl:apply-templates/>
</xsl:copy>
</xsl:template>
<xsl:template match="requirements|programm|p">
<xsl:copy>
<xsl:apply-templates/>
</xsl:copy>
</xsl:template>
<xsl:template match="div">
<xsl:copy>
<xsl:element name="systemes">
<xsl:for-each select="systemes">
<xsl:apply-templates/>
</xsl:for-each>
</xsl:element>
<xsl:apply-templates select="requirements|programm"/>
</xsl:copy>
</xsl:template>
<xsl:template match="table">
<xsl:copy>
<xsl:value-of select="."/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
You do not need to use grouping at all, Muenchian or not. I would not advise you use something as complex as keys if it is not necessary for the goal you want to achieve.
EDIT: I have used XSLT 2.0 but there is nothing in it which cannot be done in 1.0.

How to parse nested tags using XSLT in sequence?

I have below scenario for my XML.
<content>
<para>text-1 <emphasis type="bold">text-2</emphasis> text-3</para>
</content>
I want to parse it like below
<content>
<p>text-1 <b>text-2</b> text-3</p>
</content>
I have created my XSLT as below
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">
<xsl:output method="xml" encoding="ISO-8859-1" indent="no"/>
<xsl:template name="para">
<p>
<xsl:value-of select="text()" disable-output-escaping="yes"/>
<xsl:for-each select="child::*">
<xsl:if test="name()='emphasis'">
<xsl:call-template name="emphasis"/>
</xsl:if>
</xsl:for-each>
</p>
</xsl:template>
<xsl:template name="emphasis">
<xsl:if test="attribute::type = 'bold'">
<b>
<xsl:value-of select="text()" disable-output-escaping="yes"/>
</b>
</xsl:if>
</xsl:template>
<xsl:template match="/">
<content>
<xsl:for-each select="content/child::*">
<xsl:if test="name()='para'">
<xsl:call-template name="para"/>
</xsl:if>
</xsl:for-each>
</content>
</xsl:template>
</xsl:stylesheet>
XSLT provided above is generating output like below
<content>
<p>text-1 text-3<b>text-2 </b></p>
</content>
Please guide me with your suggestions, how can I get my desire output?
To do this, you just need to extend the standard Identity Transform with special cases for matching your para and emphasis elements. For example, for para elements you would the following to replace para with p and then continue matching all the child nodes
<xsl:template match="para">
<p>
<xsl:apply-templates select="#*|node()"/>
</p>
</xsl:template>
So, given the following XSLT
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes" />
<!-- This is the Identity Transform -->
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<!-- Replace para with p -->
<xsl:template match="para">
<p>
<xsl:apply-templates select="#*|node()"/>
</p>
</xsl:template>
<!-- Replace emphasis with b -->
<xsl:template match="emphasis[#type='bold']">
<b>
<xsl:apply-templates select="node()"/>
</b>
</xsl:template>
</xsl:stylesheet>
When applied to the following input XML
<content>
<para>text-1 <emphasis type="bold">text-2</emphasis> text-3</para>
</content>
The following is output
<content>
<p>text-1 <b>text-2</b> text-3</p>
</content>
You should be able to see how easy it is to extend to other cases should you input XML have more tags to transform.
do it like this ;)
<?xml version="1.0" encoding="utf-8" ?>
<xsl:stylesheet
version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
>
<xsl:template match="content">
<content><xsl:apply-templates select="para" /></content>
</xsl:template>
<xsl:template match="emphasis [#type='bold']">
<b><xsl:value-of select="." /></b>
</xsl:template>
</xsl:stylesheet>
when you do it like this the default template will catch text-1 and text-3

String comparison in XSLT

When I call this template I get the following results.
155IT Matches 155OO
155OO Matches 155OO
155PP
The XML I am processing does have three rows and those are the values, but why is the test returning true for the first two and false for the last one? How should I be doing the string comparison?
<?xml version="1.0" encoding="UTF-8"?>
<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:template name="ProofOfConcept">
<xsl:param name="Lines"/>
<xsl:param name="MainDeliveryCode"/>
<xsl:choose>
<xsl:when test="$Lines">
<xsl:variable name="CurrentDeliveryCode" select="$Lines/DLVYLOCCD"/>
<p>
<xsl:choose>
<xsl:when test=" $MainDeliveryCode = $CurrentDeliveryCode">
<xsl:value-of select="$CurrentDeliveryCode"/> Matches <xsl:value-of select="$MainDeliveryCode"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$Lines"/> Fails <xsl:value-of select="$MainDeliveryCode"/>
</xsl:otherwise>
</xsl:choose>
</p>
<xsl:call-template name="ProofOfConcept">
<xsl:with-param name="Lines" select="$Lines[position() > 1]"/>
<xsl:with-param name="MainDeliveryCode" select="$MainDeliveryCode"/>
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template match="/">
<html>
<head>
<title></title>
</head>
<body>
<xsl:call-template name="ProofOfConcept">
<xsl:with-param name="Lines" select="data/Lines/LINE"/>
<xsl:with-param name="MainDeliveryCode" select="data/header/DLVYLOCCD"/>
</xsl:call-template>
</body>
</html>
</xsl:template>
</xsl:stylesheet>
Sample data
<?xml version="1.0"
encoding="ISO-8859-1"
standalone="yes"?> <data>
<header><DLVYLOCCD>155OO</DLVYLOCCD>
</header> <Lines>
<LINE><DLVYLOCCD>155IT</DLVYLOCCD></LINE>
<LINE><DLVYLOCCD>155OO</DLVYLOCCD></LINE>
<LINE><DLVYLOCCD>155PP</DLVYLOCCD></LINE>
</Lines> </data>
Thanks for any advice.
Here is a less painful version of your XSLT:
<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:template match="data">
<html>
<head>
<title></title>
</head>
<body>
<!-- this selects the matching LINE node(s), or nothing at all -->
<xsl:apply-templates select="
Lines/LINE[DLVYLOCCD = /data/header/DLVYLOCCD]
" />
</body>
</html>
</xsl:template>
<xsl:template match="LINE">
<p>
<!-- for the sake of the example, just output a copy -->
<xsl:copy-of select="." />
</p>
</xsl:template>
</xsl:stylesheet>
gives (formatted result):
<?xml version="1.0" encoding="utf-8"?>
<html>
<head>
<title></title>
</head>
<body>
<p>
<LINE><DLVYLOCCD>155OO</DLVYLOCCD></LINE>
</p>
</body>
</html>
There are a few things wrong with your implementation. Most important, the expression:
<xsl:variable name="CurrentDeliveryCode" select="$Lines/DLVYLOCCD"/>
returns a node-set consisting of all the DLVYLOCCD elements, not just the current one as you seem to assume. Also, you shouldn't be using recursion to iterate. Use <xsl:for-each> instead, in which case you will process the items one at a time.
I figured it out.
I need to change my test to
<xsl:when test="contains($MainDeliveryCode, $CurrentDeliveryCode" >
That solved the problem.
http://www.zvon.org/xxl/XSLTreference/Output/function_contains.html is the documentation for the function.