Wrapping and moving child nodes - xslt

could you please help me transforming the following snippet?
<root>
<topic> <!-- First topic -->
<p>content1</p>
<topic> <!-- Second topic -->
<p>content2</p>
</topic>
<topic> <!-- Third topic -->
<p>content3</p>
</topic>
</topic>
</root>
I need to cut the second topic and the third topic off the first topic, wrap them in a new/empty topic and attach the new topic with its children to the root node.
<root>
<topic> <!-- First topic -->
<p>content1</p>
</topic>
<topic> <!-- New topic -->
<topic> <!-- Second topic -->
<p>content2</p>
</topic>
<topic> <!-- Third topic -->
<p>content3</p>
</topic>
</topic>
</root>
Maybe there is a very simple solution. I tried to prefix the second/suffix the third topic with a tag (looks like a very dirty solution to me), but I fail moving them.
<xsl:if test="position() = 1">
<![CDATA[
<topic>
]]>
...
</xsl:if>
<xsl:if test="position() = last()">
...
<![CDATA[
</topic>
]]>
</xsl:if>
UPDATE 1
This is a more complex and detailed example:
Source - Before Transformation
<?xml version="1.0" encoding="UTF-8"?>
<root>
<!--
The root has only a
title and multiple child
topics. but no other child
elements.
-->
<title>Root</title>
<topic id="topic_1">
<!--
Allowed child elements of 'topic'
are listed in the DITA spec.:
http://bit.ly/1ruYbdq
-->
<title>First Topic - First Level</title>
</topic>
<topic id="topic_2">
<title>Second Topic - First Level</title>
<!--
This is the main problem.
A topic must not contain
child topics AND other child
elements after the
transformation.
If a topic has child topic
AND other child elements, the
topics have to be extracted.
-->
<topic id="topic_3">
<title>Third Topic - Second Level</title>
</topic>
<topic id="topic_4">
<!--
The number of topics is not limited.
-->
<title>Fourth Topic - Second Level</title>
<topic id="topic_5">
<!--
Third level topics have to
be moved to the second
hierarchy level. No topic
may reside on the third
level after transformation.
-->
<title>Fifth Topic - Third Level</title>
</topic>
</topic>
</topic>
</root>
Result - After Transformation
<?xml version="1.0" encoding="UTF-8"?>
<root>
<title>Root</title>
<topic id="topic_1">
<title>First Topic - First Level</title>
</topic>
<topic id="topic_2">
<title>Second Topic - First Level</title>
</topic>
<!--
The third and fourth topic have
been moved extracted from the
second topic. Both (could be any
number) have been wrapped with a
dummy 'topic' element.
-->
<topic>
<!--
The second level topics have
been wrapped with a "dummy"
topic element.
-->
<topic id="topic_3">
<title>Fourth Topic - Second Level</title>
</topic>
<topic id="topic_4">
<title>Fifth Topic - Second Level</title>
</topic>
<topic id="topic_5">
<!--
The third level topic
has been moved to the
second hierarchy level.
-->
<title>Sixth Topic - Third Level</title>
</topic>
</topic>
</root>

Maybe there is a very simple solution.
Maybe there is, but it's difficult to see what's given here and what's just an example. Would this very simple solution work for you?
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="*"/>
<xsl:template match="/root">
<xsl:copy>
<topic>
<xsl:copy-of select="topic/p"/>
</topic>
<topic>
<xsl:copy-of select="topic/topic"/>
</topic>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>

Ok, this works for me:
<xsl:template match="//topic">
<xsl:choose>
<xsl:when test="topic[not(topic)]">
<!--
Wrap first level topics in a
single <section> element.
-->
<section>
<xsl:apply-templates/>
</section>
</xsl:when>
<xsl:otherwise>
<xsl:choose>
<!--
Wrap nested topics in a
<section> container.
-->
<xsl:when test="(count(preceding-sibling::topic) = 0) and (count(following-sibling::topic) >= 1)">
<!--
Close the section of the parent <topic> element.
-->
<xsl:text disable-output-escaping="yes"></section></xsl:text>
<xsl:text disable-output-escaping="yes"><section></xsl:text>
<section>
<xsl:apply-templates/>
</section>
</xsl:when>
<xsl:otherwise>
<section>
<xsl:apply-templates/>
</section>
</xsl:otherwise>
</xsl:choose>
</xsl:otherwise>
</xsl:choose>
</xsl:template>

Related

How i can get text value from body node?

I need get node if it contains text.
So when i process <p> tag - i need to check previous <topic> if it has text in body or in any child tag in <body>
With next XSL code
ancestor::topic[1]/preceding-sibling::topic[1]/body/child::node()[(self::text() and normalize-space()) or self::*][position() = last()]
But it's for some reasons not working... Why?
<topic>
<body>Topic 3 with only a paragraph, no topic title</body>
</topic>
<topic>
<body>
<p> <!-- from here -->
<image href="" />
</p> <!-- and from here -->
</body>
</topic>
<topic>
<body>Topic 5 with only a paragraph, no topic title</body>
</topic>
I think you need to tell us in more detail what you are trying and how it exactly it fails for your; when I convert your input snippet into a well-formed input document
<root>
<topic>
<body>Topic 3 with only a paragraph, no topic title</body>
</topic>
<topic>
<body>
<p> <!-- from here -->
<image href="" />
</p> <!-- and from here -->
</body>
</topic>
<topic>
<body>Topic 5 with only a paragraph, no topic title</body>
</topic>
</root>
and run it through a stylesheet matching on a p with your posted condition in a predicate it obviously finds that single p you have i.e.
<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-skip"/>
<xsl:template match="p[ancestor::topic[1]/preceding-sibling::topic[1]/body/child::node()[(self::text() and normalize-space()) or self::*][position() = last()]]">
found
</xsl:template>
</xsl:stylesheet>
outputs found as the match happens.
So explain with minimal but complete samples what your are trying, which output you expect and how it fails (i.e. which error you get or which wrong output), then we can tell perhaps what is wrong.
Sorry for the non-answer, but I couldn't stuff the code example that shows your expression does seem to work into a comment.

XSLT - Remove duplicates from mapped results

This isn't quite the threads on removing duplicates I've found on this forum.
I have a key/value map and I want to remove duplicates from the final results of the mapping.
Source Document:
<article>
<subject code="T020-060"/>
<subject code="T020-010"/>
<subject code="T090"/>
</article>
Mapping:
<xsl:variable name="topicalMap">
<topic MapCode="T020-060">Value 1</topic>
<topic MapCode="T020-010">Value 1</topic>
<topic MapCode="T090">Value 3</topic>
</xsl:variable>
Desired Result:
<article>
<topic>Value 1</topic>
<topic>Value 3</topic>
</article>
XSLT I'm working with (note, it has a testing tags and code to make sure the mapping works):
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">
<xsl:output method="xml" encoding="utf8" indent="yes" exclude-result-prefixes="#all"/>
<xsl:template match="article">
<article>
<xsl:for-each-group select="subject" group-by="$topicalMap/topic[#MapCode = #code]">
<test-group>
<code>Current code: <xsl:value-of select="#code"/></code>
<topic>Current keyword: <xsl:value-of
select="$topicalMap/topic[#MapCode = #code]"/></topic>
</test-group>
</xsl:for-each-group>
<simple-mapping><xsl:apply-templates/></simple-mapping>
</article>
</xsl:template>
<!-- Simple Mapping Topics -->
<xsl:template match="subject">
<xsl:variable name="ArticleCode" select="#code"/>
<topic>
<xsl:value-of select="$topicalMap/topic[#MapCode = $ArticleCode]"/>
</topic>
</xsl:template>
<!-- Keyword Map -->
<xsl:variable name="topicalMap">
<topic MapCode="T020-060">Value 1</topic>
<topic MapCode="T020-010">Value 1</topic>
<topic MapCode="T090">Value 3</topic>
</xsl:variable>
</xsl:stylesheet>
Doing the group-by that way produces nothing. If I duplicate the topics in the source document and do group-by="#code" that works to remove before applying the mapping. But I want to remove resultant duplicate values not duplicate keys.
Simple-mapping stuff is just to show working code.
Use
<xsl:for-each-group select="subject" group-by="$topicalMap/topic[#MapCode = current()/#code]">
<topic>
<xsl:value-of select="current-grouping-key()"/>
</topic>
</xsl:for-each-group>
or better yet
<xsl:key name="map" match="topic" use="#MapCode"/>
<xsl:template match="article">
<article>
<xsl:for-each-group select="subject" group-by="key('map', #code, $topicalMap)">
<topic>
<xsl:value-of select="current-grouping-key()"/>
</topic>
</xsl:for-each-group>
</article>
</xsl:template>

Recursively replacing elements in XSLT

I need to replace the <tref> element with other tags from elsewhere in my document. For example, I have:
<tref id="57236"/>
and
<Topic>
<ID>57236</ID>
<Text>
<p id="4">
<cs id="56792">1090-189-01 </cs>
<href id="57237">
<cs id="56792">Document Name</cs>
</href>
</p>
</Text>
</Topic>
Obtaining the following is not a problem:
<p id="4">
<cs id="56792">1090-189-01 </cs>
<href id="57237">
<cs id="56792">Document Name</cs>
</href>
</p>
With this stylesheet:
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="tref">
<xsl:variable name="NodeID"><xsl:value-of select="#id"/></xsl:variable>
<xsl:copy-of select="//Topic[ID = $NodeID]/Text/p/node()"/>
</xsl:template>
What I cannot do is replacing trefs nested into other trefs. For example, consider the following:
<tref id="57236"/>
and:
<Topic>
<ID>57236</ID>
<Text>
<p id="251">
<tref id="37287"/>
</p>
</Text>
</Topic>
My stylesheet duly replaces the tref with the content of the tag - which also contains a tref:
<p id="251">
<tref id="37287"/>
</p>
My current solution is to call <xsl:template match="tref"> from two different stylesheets. It does the job, but it is not very elegant, and what if trefs are nested at an even deeper level? And recursion is the bread and butter of XSLT.
Is there a solution to recursively replace all trefs as in XSLT?
Instead of using xsl:copy-of, use xsl:apply-templates
<xsl:apply-templates select="//Topic[ID = $NodeID]/Text/p/node()"/>
Or, to eliminate the use of the varianle
<xsl:apply-templates select="//Topic[ID = current()/#id]/Text/p/node()"/>
Note you can make use of an xsl:key to look-up the Topic elements
<xsl:key name="topic" match="Topic" use="ID" />
Then you can write this
<xsl:apply-templates select="key('topic', #id)/Text/p/node()"/>
Be wary of infinite recursion if you have a tref referring to a Topic that is an ancestor of it.

Apply templates not working as desired

Given the following xml inputs:
file1:
<?xml version="1.0" encoding="UTF-8"?>
<File1 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<code code="file1_code" displayName="file1_display" codeSystem="file1_cs" codeSystemName="file1_csn"/>
<title>Title of file1</title>
<component typeCode="COMP">
<structuredBody classCode="DOCBODY">
<component typeCode="COMP">
<section>
<templateId root="someRoot_file1" assigningAuthorityName="someAuhthority_file1"/>
<code code="file1-sec1_code" displayName="file1_sec1_display" codeSystem="file1_sec1_cs" codeSystemName="file1_sec1_csn"/>
<title>Tile of sec 1 from file1</title>
<text>
<content styleCode="Italics">
Text of sec 1 from file1
</content>
</text>
<entry> file 1 sec 1
</entry>
</section>
</component>
<component typeCode="COMP">
<section classCode="DOCSECT">
<code code="file1_sec2_code" codeSystem="file2_sec2_cs" displayName="file2_sec2_display" codeSystemName="file2_sec2_csn"/>
<title>Tile from sec 2 file 1</title>
<text>
<content styleCode="Italics">
Text from file1 sec 2
</content>
</text>
<entry typeCode="test"> file2 sec 2
</entry>
</section>
</component>
</structuredBody>
</component>
</File1>
file2:
<?xml version="1.0"?>
<A>
<title value="Title of file2"/>
<text>
<status value="generated"/>
<div xmlns="http://www.w3.org/1999/xhtml">
<p>File 2 Text</p>
</div>
</text>
<section>
<code>
<coding>
<system value="sec 1 file2 sys"/>
<code value="sec 1 file 2 code"/>
<display value="sec 1 file 2 display"/>
</coding>
</code>
<title>Title of sec 1 file2</title>
<text>
<content styleCode="Italics">Section 1 Text
</content>
</text>
<entry>
<someEntry>
</someEntry>
</entry>
</section>
<section>
<code>
<coding>
<system value="sec 2 file2 sys"/>
<code value="sec 2 file 2 code"/>
<display value="sec 2 file 2 display"/>
</coding>
</code>
<title>Title of sec 2 file2</title>
<text>
<content styleCode="Italics">Section 2 file2 Text
</content>
</text>
<entry>
<someEntry> entry sec 2 file 2
</someEntry>
</entry>
</section>
</A>
and the following xslt:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes"/>
<xsl:strip-space elements="*" />
<xsl:variable name="input" select="/" />
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="/">
<Bundle>
<id value="test"/>
<type value="document"/>
<entry>
<resource>
<xsl:apply-templates select="document('file2.xml')/*"/>
</resource>
</entry>
</Bundle>
</xsl:template>
<xsl:template match="text">
<text>
<status value="generated"/>
<div xmlns="http://www.w3.org/1999/xhtml">
<p>This is the text from the stylesheet </p>
</div>
</text>
</xsl:template>
<xsl:template match="title">
<xsl:apply-templates select="$input/File1/title"/>
</xsl:template>
<xsl:template match="section[1]">
<xsl:apply-templates select="$input/File1/component/structuredBody/component/section"/>
</xsl:template>
<xsl:template match="section[2]"/>
<xsl:template match="File1/title">
<title>
<xsl:attribute name="value">
<xsl:value-of select="." />
</xsl:attribute>
</title>
</xsl:template>
<xsl:template match = "File1/component/structuredBody/component/section">
<section>
<xsl:apply-templates/>
</section>
</xsl:template>
</xsl:stylesheet>
And this is the output:
<?xml version="1.0" encoding="UTF-8"?>
<Bundle>
<id value="test"/>
<type value="document"/>
<entry>
<resource>
<A>
<title value="Title of file1"/>
<text>
<status value="generated"/>
<div xmlns="http://www.w3.org/1999/xhtml">
<p>This is the text from the stylesheet </p>
</div>
</text>
<section>
<templateId xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" root="someRoot_file1" assigningAuthorityName="someAuhthority_file1"/>
<code xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" code="file1-sec1_code" displayName="file1_sec1_display" codeSystem="file1_sec1_cs" codeSystemName="file1_sec1_csn"/>
<title value="Title of file1"/>
<text>
<status value="generated"/>
<div xmlns="http://www.w3.org/1999/xhtml">
<p>This is the text from the stylesheet </p>
</div>
</text>
<entry xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"/>
</section>
<section>
<code xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" code="file1_sec2_code" codeSystem="file2_sec2_cs" displayName="file2_sec2_display" codeSystemName="file2_sec2_csn"/>
<title value="Title of file1"/>
<text>
<status value="generated"/>
<div xmlns="http://www.w3.org/1999/xhtml">
<p>This is the text from the stylesheet </p>
</div>
</text>
<entry xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" typeCode="test"/>
</section>
</A>
</resource>
</entry>
</Bundle>
And this is the expected output:
<?xml version="1.0" encoding="UTF-8"?>
<Bundle>
<id value="test"/>
<type value="document"/>
<entry>
<resource>
<A>
<title value="Title of file1"/>
<text>
<status value="generated"/>
<div xmlns="http://www.w3.org/1999/xhtml">
<p>This is the text from the stylesheet </p>
</div>
</text>
<section>
<templateId xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" root="someRoot_file1" assigningAuthorityName="someAuhthority_file1"/>
<code xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" code="file1-sec1_code" displayName="file1_sec1_display" codeSystem="file1_sec1_cs" codeSystemName="file1_sec1_csn"/>
<title>Tile of sec 1 from file1</title>
<text>
<content styleCode="Italics">
Text of sec 1 from file1
</content>
</text>
<entry xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"/>
</section>
<section>
<code xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" code="file1_sec2_code" codeSystem="file2_sec2_cs" displayName="file2_sec2_display" codeSystemName="file2_sec2_csn"/>
<title>Tile from sec 2 file 1</title>
<text>
<content styleCode="Italics">
Text from file1 sec 2
</content>
</text>
<entry xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" typeCode="test"/>
</section>
</A>
</resource>
</entry>
</Bundle>
I have the following questions:
Why is the title in the section elements coming from the main title (i.e. File1/title) when the apply templates is within File1/component/structuredBody/component/section? I was expecting that the title of the section will be output, which is what is desired. Even more confusing is that it does indeed output the elements in the section like code, entry and so on but title and text (see q2 below) seems to be treated differently and I can't for the life of me understand why.
Same with text. Why is the text for section not being output?
Here is my presumably false understanding of the process:
We start with the <xsl:template match="/"> and create elements Bundle, id etc. and then using <xsl:apply-templates select="document('file2.xml')/*"/> we match the top element of file2 (A) and since we don't have a template matching it explicitly, the identity template is called, copies it and process its child elements, which are text, title and section. For each of these child elements, it looks for a matching template. it finds them and matches them.
For element section however, it matches only the first section element because of <xsl:template match="section[1]"> and then because of <xsl:apply-templates select="$input/File1/component/structuredBody/component/section"/> in the template, it looks for a template matching children of section in FIle1, which are code, text, title and templateId. It finds no such explicitly defined template, so calls the identity templates for them, copies and processes them till the end. At least that is my understanding of it.
Why is the title in the section elements coming from the main title
Because any time the processor is instructed to apply templates to a title, it looks for the best-matching template to apply, and finds this:
<xsl:template match="title">
<xsl:apply-templates select="$input/File1/title"/>
</xsl:template>
This changes the context to the title in File1.xml, and the best-matching template for this one is:
<xsl:template match="File1/title">
<title>
<xsl:attribute name="value">
<xsl:value-of select="." />
</xsl:attribute>
</title>
</xsl:template>
and that is the result you see.
Same with text. Why is the text for section not being output?
-- edited in response to the following clarification: --
When I say text I am talking about text elements only.
The original text element (child of section in File1.xml) is not being output because you have a specific template matching it and outputting something else instead:
<xsl:template match="text">
<text>
<status value="generated"/>
<div xmlns="http://www.w3.org/1999/xhtml">
<p>This is the text from the stylesheet </p>
</div>
</text>
</xsl:template>
# michael.hor257k and #Michael Kay yeah, that was definitely the case, that I misunderstood how xsl:apply-templates works with regards to context . I thought because I called the xsl:apply-templates from within xsl:template match = "$input/File1/component/structuredBody/component/section"> that it will only look for match templates that match the children of section. In other words, I thought it will look for templates like “<xsl:template match=”File1/component/structuredBody/component/section/title"> but that is clearly not the case.
xsl:apply-templates simply looks for the children and then looks for a match template regardless of the context from which they were called. So, it will look for title or text template that matches and if it finds them, it will match them.
The easiest solution I could find that seems to solve the problem is to add a path to the title and text templates. In other words, instead of just <xsl:template match="text"> I should have <xsl:template match="A/text">. Same for title. This way, the xsl:apply-templates will not apply <xsl:template match="A/text"> as the title in section is not a child of A. So given that no matching explicit template is defined, the identity template will be applied and will output the title of section as desired.

check for the following node

I've a small question in XSLT. i need XPath to validate a condition. and below is my XML.
<root>
<para>
Erlanger and several associates formed a syndicate to acquire the lease of an island in the West Indies for £55,000. The idea was to mine the <page num="44"/>island for phosphates.
</para>
<para>
<content-style font-style="bold">2.25</content-style> A commission or payment that a promoter receives upon transfer of property to a company must also be disclosed.
<para>
board were all nominees of Green and Smith; <page num="45"/>accordingly, disclosure </para>
</para>
<para>
<content-style font-style="bold">2.26</content-style> If a promoter contracts with the company whether as vendor<footnote num="57" id="fn57">
<para>
<case>
<casename>
<content-style font-style="italic">Re Leeds & Hanley Theatres of Varieties Ltd</content-style>
</casename> [1902] Ch 809 (Court of Appeal, England)
</case>.
</para>
</footnote> or purchaser,<footnote num="58" id="fn58">
<para>
<case>
<casename>
<content-style font-style="italic">Habib Abdul Rahman v Abdul Cader</content-style>
</casename> (1886) 4 Ky 193 (High Court of the Straits Settlements)
</case>.
</para>
</footnote> the fact that he is a contractor must be disclosed.
</para>
</root>
and XSL is as below.
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:fn="http://www.w3.org/2005/xpath-functions"
xmlns:ntw="Number2Word.uri"
exclude-result-prefixes="ntw">
<xsl:output method="html"/>
<xsl:strip-space elements="*"/>
<xsl:variable name="ThisDocument" select="document('')"/>
<xsl:template match="/">
<xsl:text disable-output-escaping="yes"><![CDATA[<!DOCTYPE html>]]></xsl:text>
<html>
<head>
<xsl:text disable-output-escaping="yes"><![CDATA[</meta>]]></xsl:text>
<title>
<xsl:value-of select="chapter/title[1]/*"/>
</title>
<link rel="stylesheet" href="C:\Users\u0138039\Desktop\Proview\SG\Commentary_SG_XML-03032014\SG-Business Guide to Competition Law\05192014\XSLT\main.css" type="text/css"/>
<xsl:text disable-output-escaping="yes"><![CDATA[</link>]]></xsl:text>
</head>
<body>
<xsl:apply-templates/>
<xsl:if test="//footnote">
<section class="tr_footnotes">
<hr/>
<xsl:apply-templates select="//page[not(ancestor::toc)]| //footnote" mode="footnote"/>
</section>
</xsl:if>
</body>
</html>
</xsl:template>
<xsl:template match="footnote">
<xsl:variable name="varHeaderNote" select='concat("f",#num)'/>
<xsl:variable name="varFootNote" select='concat("#ftn.",#num)'/>
<sup>
<a name="{$varHeaderNote}" href="{$varFootNote}" class="tr_ftn">
<xsl:value-of select="#num"/>
</a>
</sup>
</xsl:template>
<xsl:template match="page" mode="footnote">
<xsl:processing-instruction name="pb">
<xsl:text>label='</xsl:text>
<xsl:value-of select="./#num"/>
<xsl:text>'</xsl:text>
<xsl:text>?</xsl:text>
</xsl:processing-instruction>
</xsl:template>
<xsl:template match="footnote" mode="footnote">
<div class="tr_footnote">
<div class="footnote">
<sup>
<a>
<xsl:attribute name="name">
<xsl:text>ftn.</xsl:text>
<xsl:value-of select="#num"/>
</xsl:attribute>
<xsl:attribute name="href">
<xsl:text>#f</xsl:text>
<xsl:value-of select="#num"/>
</xsl:attribute>
<xsl:attribute name="class">
<xsl:text>tr_ftn</xsl:text>
</xsl:attribute>
<xsl:value-of select="#num"/>
</a>
</sup>
<xsl:apply-templates/>
</div>
</div>
</xsl:template>
</xsl:stylesheet>
here when i run this, i get both <?pb label='44'?><?pb label='45'?>
where as i need as condition as below.
there should only be a `footnote` following `page` and there should be no `page` between `page` and `footnote`
in simple, by taking the above example, there are two page, by ignoring all other tags and considering only page the structure looks like below.
page num='44'
page num='45'
footnote
here i want only page num='45' to be captured and leave page num='44' since page num='44' is followed by another page but not directly footnote, this is pretty confusing, please let me know how can i do this.
The demo can be found here
Thanks
To capture only pages that contain at least one footnote, you could use a test like
(following::page | following::footnote)[1][self::footnote]
i.e. take all the following page and footnote elements in document order, and check whether the first one of these elements is a footnote - if it isn't then either there's an intervening page or there are no more page or footnote elements at all, either way we know there are no footnotes on this page.
<xsl:template match="page[(following::page | following::footnote)[1][self::footnote]]" mode="footnote">
<xsl:processing-instruction name="pb">
<xsl:text>label='</xsl:text>
<xsl:value-of select="./#num"/>
<xsl:text>'</xsl:text>
<xsl:text>?</xsl:text>
</xsl:processing-instruction>
</xsl:template>
<xsl:template match="page" mode="footnote" />
in simple, by taking the above example, there are two page, by
ignoring all other tags and considering only page the structure
looks like below.
page num='44'
page num='45'
footnote
here i want only page num='45' to be captured and leave page
num='44' since page num='44' is followed by another page but not
directly footnote
To select pages that are immediately followed by a footnote, use:
page[following-sibling::*[1][self::footnote]]
If a footnote is always preceded by a page, you could also use:
footnote/preceding-sibling::page[1]
Edit:
In your real example, where pages and footnotes are not siblings, you should use Ian's answer, i.e :
page[(following::page | following::footnote)[1][self::footnote]]
or (assuming that there is only block of footnotes):
footnote[1]/preceding::page[1]
When you match a page you can check whether the next footnote has a preceding page which is the current page. If it's not, then you don't print out its processing instruction since it's a page without a footnote.
<xsl:template match="page" mode="footnote">
<xsl:if test="following::footnote[1][preceding::page[1]/#num = current()/#num]">
<xsl:processing-instruction name="pb">
<xsl:text>label='</xsl:text>
<xsl:value-of select="./#num"/>
<xsl:text>'</xsl:text>
<xsl:text>?</xsl:text>
</xsl:processing-instruction>
</xsl:if>
</xsl:template>
See: http://xsltransform.net/eiQZDbt/3