Create a list of element from nested elements in xslt? - xslt-2.0

I have a document with nested elements.Now I want to list them one element after another element using XSLT 2.0
Here is the input:
<?xml version="1.0" encoding="UTF-8"?>
<a>
<b>
test text
<b>
this text is in b
</b>
</b>
<b>
this text is
<b>
this text is test
</b>
</b>
</a>
This is what I expect:
<a>
<b>test text</b>
<b>this text is in b</b>
<b>this text is </b>
<b>this text is test</b>
</a>
I have no any idea to do this..I tried grouping concept..but it was not succeeded.Please help me to resolve this.

try this
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:strip-space elements="*"/>
<xsl:output indent="yes" omit-xml-declaration="yes"/>
<xsl:template match="*|#*">
<xsl:copy>
<xsl:apply-templates select="node()|#*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="b[b]">
<xsl:copy>
<xsl:apply-templates select="text()"/>
</xsl:copy>
<xsl:apply-templates select="*"/>
</xsl:template>
<xsl:template match="text()">
<xsl:value-of select="normalize-space(.)"/>
</xsl:template>
</xsl:stylesheet>

Related

Need to add 'br' tag after the last <group> element under 'block' element

I want to add 'br' tag after 'group' element under the block element. Basically after the 'group' element in we want break page, that's why we are trying to add break page in output. Below is our input xml structure.
I tried on below XSLT code but unable to get result, please help on this issue:
Input XML:
<?xml version="1.0" encoding="UTF-8"?>
<page>
<stream>
<block>
<group>content here</group>
<group>content here</group>
<group>content here</group>
</block>
</stream>
<stream>
<block>
<group>content here</group>
<group>content here</group>
<group>content here</group>
<!-- please add here br tag -->
</block>
</stream>
</page>
XSLT CODE:
<?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="2.0">
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#* | node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="group">
<xsl:element name="p">
<xsl:apply-templates/>
<xsl:if test="position() = last()">
<br></br>
</xsl:if>
</xsl:element>
</xsl:template>
</xsl:stylesheet>
Output:
<?xml version="1.0" encoding="UTF-8"?><page>
<stream>
<block>
<p>content here</p>
<p>content here</p>
<p>content here</p>
</block>
</stream>
<stream>
<block>
<p>content here</p>
<p>content here</p>
<p>content here</p>
<!-- please add here br tag -->
</block>
</stream>
</page>
I would match on <xsl:template match="group[last()]"> e.g.
<xsl:template match="group[last()]">
<p>
<xsl:apply-templates/>
</p>
<br/>
</xsl:template>
and for the other groups it seems you want
<xsl:template match="group">
<p>
<xsl:apply-templates/>
</p>
</xsl:template>
It is not clear, however, why your verbal description asks to add a br after the last group of a block while your sample has two blocks and you only add the br in the last block of the last stream. So perhaps you want <xsl:template match="stream[last()]/block/group[last()]"> e.g.
<xsl:template match="stream[last()]/block/group[last()]">
<p>
<xsl:apply-templates/>
</p>
<br/>
</xsl:template>

Keeping track of an iterator through a nested xsl:for-each

I want to take the following input:
<test>
<a>
<b />
<b />
</a>
<a>
<b />
<b />
</a>
</test>
And create the following output using XSLT 2.0:
<items>
<item num="1">
<item num="2"/>
<item num="3"/>
</item>
<item num="4">
<item num="5"/>
<item num="6"/>
</item>
</items>
I know this is wrong, but for a starting point, here's my current XSLT:
<xsl:template match="test">
<items>
<xsl:for-each select="a">
<item num="{position()}">
<xsl:for-each select="b">
<item num="{position()}"/>
</xsl:for-each>
</item>
</xsl:for-each>
</items>
</xsl:template>
This is clearly not the way to do it, because position() only considers elements in the same level. But how would I do this?
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:template match="/*">
<items><xsl:apply-templates/></items>
</xsl:template>
<xsl:template match="a|b">
<xsl:variable name="vNum">
<xsl:number level="any" count="a|b"/>
</xsl:variable>
<item num="{$vNum}">
<xsl:apply-templates/>
</item>
</xsl:template>
</xsl:stylesheet>
when applied on the provided XML document:
<test>
<a>
<b />
<b />
</a>
<a>
<b />
<b />
</a>
</test>
produces the wanted, correct result:
<items>
<item num="1">
<item num="2"/>
<item num="3"/>
</item>
<item num="4">
<item num="5"/>
<item num="6"/>
</item>
</items>
In XSLT 2, using xsl:number is the right way, if your XSLT processor also supports XSLT 3, then using an accumulator https://www.w3.org/TR/xslt-30/#element-accumulator is an alternative:
<?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="#all"
version="3.0">
<xsl:mode on-no-match="shallow-copy" use-accumulators="item-count"/>
<xsl:accumulator name="item-count" as="xs:integer" initial-value="0">
<xsl:accumulator-rule match="test" select="0"/>
<xsl:accumulator-rule match="test/a | test/a/b" select="$value + 1"/>
</xsl:accumulator>
<xsl:template match="test">
<items>
<xsl:apply-templates/>
</items>
</xsl:template>
<xsl:template match="a | b">
<item num="{accumulator-before('item-count')}">
<xsl:apply-templates/>
</item>
</xsl:template>
</xsl:stylesheet>
https://xsltfiddle.liberty-development.net/3NSSEvn
That would even work with streaming in Saxon EE where xsl:number is not supported:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
exclude-result-prefixes="#all"
version="3.0">
<xsl:mode on-no-match="shallow-copy" use-accumulators="item-count" streamable="yes"/>
<xsl:accumulator name="item-count" as="xs:integer" initial-value="0" streamable="yes">
<xsl:accumulator-rule match="test" select="0"/>
<xsl:accumulator-rule match="test/a | test/a/b" select="$value + 1"/>
</xsl:accumulator>
<xsl:template match="test">
<items>
<xsl:apply-templates/>
</items>
</xsl:template>
<xsl:template match="a | b">
<item num="{accumulator-before('item-count')}">
<xsl:apply-templates/>
</item>
</xsl:template>
</xsl:stylesheet>
I figured out one solution: use xsl:number with any level and counting both a and b:
<xsl:template match="test">
<items>
<xsl:for-each select="a">
<item>
<xsl:attribute name="num">
<xsl:number level="any" count="a|b"/>
</xsl:attribute>
<xsl:for-each select="b">
<item>
<xsl:attribute name="num">
<xsl:number level="any" count="a|b"/>
</xsl:attribute>
</item>
</xsl:for-each>
</item>
</xsl:for-each>
</items>
</xsl:template>

update more than one result in same uri

I want to update the same result document to accumulate all the meta elements. I have tried to recreate the multiple dita-ot templates to explain the problem here. My question is, is it possible to update keyword.xml in the
<xsl:template match="html" mode="pages">
template itself? maybe using xsl:stream or xsl:accumulator? XSLT 3 and Saxon-HE-9.8.0-12
Input XML
<root>
<article>
<html name="firsthtm">
<head>Head1</head>
<meta>keyword;firsthtm</meta>
</html>
<html name="secondhtm">
<head>Head2</head>
<meta>keyword;secondhtm</meta>
</html>
<html name="thirdhtm">
<head>Head3</head>
<meta>keyword;thirdhtm</meta>
</html>
</article>
XSLT
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema" exclude-result-prefixes="xs" version="3.0">
<xsl:template match="/">
<xsl:apply-templates/>
</xsl:template>
<xsl:template match="article">
<xsl:apply-templates mode="pages"/>
</xsl:template>
<xsl:template match="html" mode="pages">
<xsl:result-document href="{#name}.html">
<html>
<title>
<xsl:value-of select="#name"/>
</title>
</html>
</xsl:result-document>
<!-- update keyword.xml for each html -->
<xsl:result-document href="keyword.xml">
<root>
<xsl:copy-of select="meta"/>
</root>
</xsl:result-document>
</xsl:template>
firsthtm.htm
<html>
<title>firsthtm</title>
</html>
secondhtm.htm
<html>
<title>secondhtm</title>
</html>
thirdhtm.htm
<html>
<title>thirdhtm</title>
</html>
keyword.xml
<root>
<meta>keyword;secondhtm</meta>
<meta>keyword;secondhtm</meta>
<meta>keyword;thridhtm</meta>
</root>
Just create the result document in the template matching article:
<xsl:template match="article">
<xsl:apply-templates mode="pages"/>
<xsl:result-document href="keyword.xml">
<root>
<xsl:copy-of select="html/meta"/>
</root>
</xsl:result-document>
</xsl:template>
If you want to use the match="html" mode="pages" then you have to decide on which match you want to construct that result e.g on the first
<xsl:template match="html" mode="pages">
<xsl:result-document href="{#name}.html">
<html>
<title>
<xsl:value-of select="#name"/>
</title>
</html>
</xsl:result-document>
<!-- update keyword.xml for first html -->
<xsl:variable name="html-index" as="xs:integer">
<xsl:number/>
</xsl:variable>
<xsl:if test="$html-index = 1">
<xsl:result-document href="keyword.xml">
<root>
<xsl:copy-of select="ancestor::article/html/meta"/>
</root>
</xsl:result-document>
</xsl:if>
</xsl:template>
In simple cases (there are only those html element children for the article and you have used xsl:strip-space) it might suffice to simply test <xsl:if test="position() = 1">.
A simple solution would be moving the xsl:result-document to the article template and copy all html/meta elements from there:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema" exclude-result-prefixes="xs" version="3.0">
<xsl:template match="/">
<xsl:apply-templates/>
</xsl:template>
<xsl:template match="article">
<xsl:apply-templates mode="pages"/>
<xsl:result-document href="keyword.xml">
<root>
<xsl:copy-of select="html/meta"/>
</root>
</xsl:result-document>
</xsl:template>
<xsl:template match="html" mode="pages">
<xsl:result-document href="{#name}.html">
<html>
<title>
<xsl:value-of select="#name"/>
</title>
</html>
</xsl:result-document>
<!-- update keyword.xml for each html -->
</xsl:template>
</xsl:stylesheet>

How to remove Soap namespace associated with attribute

I have to remove the soap name space associated with ConfigR(attribute).
Inside XSLT I am using XSL Copy and hence exclude prefix is not working.
I have tried below, but not working.Please can anyone suggest.
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" >
<xsl:output indent="yes"/>
<xsl:template match="#* | node()">
<xsl:copy>
<xsl:apply-templates select="#* | node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="soapenv:*">
<xsl:apply-templates select="#* | node()"/>
</xsl:template>
</xsl:stylesheet>
Input -
<soapenv:Envelope
xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
<soapenv:Body>
<ConfigR>
Inside I have some more input.
</ConfigR>
</soapenv:Body>
</soapenv:Envelope>
Now in Output I am getting:
<ConfigR xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
You can replace you existing template:
<xsl:template match="#* | node()">
<xsl:copy>
<xsl:apply-templates select="#* | node()" />
</xsl:copy>
</xsl:template>
with following:
<xsl:template match="*">
<xsl:element name="{local-name(.)}">
<xsl:apply-templates select="#* | node()" />
</xsl:element>
</xsl:template>
<xsl:template match="#*">
<xsl:attribute name="{local-name(.)}">
<xsl:value-of select="." />
</xsl:attribute>
</xsl:template>
You can find it here

Getting the value of sibling nodes in XSLT

I have the following XML
<CN>12<CN>
<CT>XYXY</CT>
I need the result AS
<DIV>12 XYXY</DIV>
I'm USING the floowing XSLT but it's not working
<xsl:variable name="x"><xsl:value-of select="CN"/></xsl:variable>
<xsl:template match="CT">
<div class="chap-title"><span><xsl:value-of select="$x"/></span></div>
</xsl:template>
Input:
<input>
<CN>12</CN>
<CT>XYXY</CT>
</input>
XSLT:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="2.0">
<xsl:output indent="yes"
method="xml"
encoding="UTF-8" />
<xsl:template match="/input">
<DIV>
<xsl:value-of select="CN"/>
<xsl:text> </xsl:text>
<xsl:value-of select="CT"/>
</DIV>
</xsl:template>
</xsl:stylesheet>

Resources