xsl:copy-of excluding parent - xslt

What code could I use in replace of <xsl:copy-of select="tag"/>, that when applied to the following xml..
<tag>
content
<a>
b
</a>
</tag>
..would give the following result: ?
content
<a>
b
</a>
I wish to echo out all the content therein, but excluding the parent tag
Basically I have several sections of content in my xml file, formatted in html, grouped in xml tags
I wish to conditionally access them & echo them out
For example: <xsl:copy-of select="description"/>
The extra parent tags generated do not affect the browser rendering, but they are invalid tags, & I would prefer to be able to remove them
Am I going about this in totally the wrong way?

Since you want to include the content part as well, you'll need the node() function, not the * operator:
<xsl:copy-of select="tag/node()"/>
I've tested this on the input example and the result is the example result:
content
<a>
b
</a>
Without hard-coding the root node name, this can be:
<xsl:copy-of select="./node()" />
This is useful in situations when you are already processing the root node and want an exact copy of all elements inside, excluding the root node. For example:
<xsl:variable name="head">
<xsl:copy-of select="document('head.html')" />
</xsl:variable>
<xsl:apply-templates select="$head" mode="head" />
<!-- ... later ... -->
<xsl:template match="head" mode="head">
<head>
<title>Title Tag</title>
<xsl:copy-of select="./node()" />
</head>
</xsl:template>

Complementing Welbog's answer, which has my vote, I recommend writing separate templates, along the lines of this:
<xsl:template match="/">
<body>
<xsl:apply-templates select="description" />
</body>
</xsl:template>
<xsl:template match="description">
<div class="description">
<xsl:copy-of select="node()" />
</div>
</xsl:template>

Related

Change attribute value to position of another element with corresponding attribute value

I have a single XHTML document that contains span and div elements that refer to page breaks of a print version using id and epub:type attributes. For example: <div epub:type="pagebreak" id="page-3"/>. The document also has links to those elements, for example: 3.
This single XHTML document will be split into multiple XHTML documents to form an EPUB package. For this reason, the href attributes need to be updated to match the new location of the corresponding id. For example: 3. The name of the new XHTML file is equal to the position of the body/section elements. So in the last example, the page break with id="page-3" is apparently in the second body/section element.
I'm using the following XSLT 2.0 stylesheet:
<!--identity transform-->
<xsl:template match="#* | node()">
<xsl:copy>
<xsl:apply-templates select="#* | node()"/>
</xsl:copy>
</xsl:template>
<!--variable to match id of elements with pagebreak values-->
<xsl:variable name="page-id" select="//*[#epub:type = 'pagebreak']/#id"/>
<!--update href attributes to match new filenames-->
<xsl:template match="a/#href">
<xsl:choose>
<xsl:when test="tokenize(., '#')[last()] = $page-id">
<xsl:attribute name="href">
<xsl:number count="//body/section[$page-id = tokenize(., '#')[last()]]" format="01"/>
<xsl:value-of select="concat('.xhtml', .)"/>
</xsl:attribute>
</xsl:when>
<xsl:otherwise>
<xsl:copy-of select="."/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
It checks for href attributes that have a corresponding id using the $page-id variable. If there is a match, the href attribute should be updated using the count() function. Otherwise, the href should remain unchanged. The test seems to work, however, I'm not getting the result I want. This is the input:
<body>
<section>
<p>Link to page 3: 3</p>
</section>
<section>
<div epub:type="pagebreak" id="page-3"/>
</section>
</body>
This is the output I get:
<body>
<section>
<p>Link to page 3: 3</p>
</section>
<section>
<div epub:type="pagebreak" id="page-3"/>
</section>
</body>
This is the output I want:
<body>
<section>
<p>Link to page 3: 3</p>
</section>
<section>
<div epub:type="pagebreak" id="page-3"/>
</section>
</body>
It seems as if the XPath expression within xsl:number doesn't return a result, but I can't figure out why. Can anyone help me with this please?
I think you want e.g.
<xsl:template match="body/section" mode="number">
<xsl:number format="01"/>
<xsl:template>
and then instead of
<xsl:number count="//body/section[$page-id = tokenize(., '#')[last()]]" format="01"/>
use
<xsl:apply-templates select="key('page-id', substring-after(., '#'))" mode="number"/>
plus a key declaration
<xsl:key name="page-id" match="body/section" use=".//*[#epub:type = 'pagebreak']/#id"/>

Attempts to use following-sibling to convert

I try to convert my old html by xslt-script to my new xml stucture.
I have a Problem to converting the folowing source to my needed xml structure.
Source
<p>
<a class="DropDown">Example Text</a>
</p>
<div class="collapsed">
<table>..</table>
<p>..</p>
</div>
xml structure
<lq>
<p>Example Text</p>
<table>..</table>
<p>..</p>
</lp>
I tried the following xls, but the div class="collapsed" is not adopted into the lp tag.
<xsl:template match="p/a[#class='DropDown']">
<lp>
<p><xsl:apply-templates select="text()"/></p>
<xsl:if test="/p/a/following-sibling::*[1][self::div]">
<xsl:apply-templates select="*|text()"/>
</xsl:if>
</lp>
</xsl:template>
Can anyone tell me what I did wrong ore where the mistake is?
Thanks much
IMHO, you want to do:
<!-- identity transform -->
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="p[a/#class='DropDown']">
<lp>
<p>
<xsl:value-of select="a"/>
</p>
<xsl:copy-of select="following-sibling::*[1][self::div]/node()"/>
</lp>
</xsl:template>
<xsl:template match="div[preceding-sibling::*[1][self::p/a/#class='DropDown']]"/>
As for your mistake:
You are testing the existence of some p that is the root element
and contains an a whose following sibling is div. None of these are true in the given example;
xsl:if does not change the context: your <xsl:apply-templates
select="*|text()"/> applies templates to the child nodes of the
current a;
Presumably you don't want the div to appear again in the original place -
so if you have another template to suppress it, you cannot use
<xsl:apply-templates> to insert it at the place you do want it -
at least not without using another mode.

Merging XSLTs into one flle and calling each template separately matching specific content

I've the below Sample XMLs.
XML1
<root num="1">
<abc></abc>
<cde></cde>
<def></def>
</root>
XML2
<root num="2">
<xyz></xyz>
<cft></cft>
<vft></vft>
</root>
XML3
<root num="3">
<dfg></dfg>
<mnb></mnb>
<gft></gft>
<root>
And i have 3 different XSLTs, each corresponding to XML.
I want to achieve the below.
Make a single XSLT and call the template based on the root number. something like the below.
<xsl:if test="root[#num="1"]>
<!--Call the template matching root 1-->
</xsl:if>
<xsl:if test="root[#num="2"]>
<!--Call the template matching root 2-->
</xsl:if>
<xsl:if test="root[#num="3"]>
<!--Call the template matching root 3-->
</xsl:if>
I just want to put all the XSLTs in a single XSLT, please let me know how can i do this.
Thanks
You can use element.
The element contains rules to apply when a specified node is matched.
The match attribute is used to associate the template with an XML element. The match attribute can also be used to define a template for a whole branch of the XML document (i.e. match="/" defines the whole document).
Note:  is a top-level element.
Example:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
<html>
<body>
<h2>My CD Collection</h2>
<xsl:apply-templates/>
</body>
</html>
</xsl:template>
<xsl:template match="cd">
<p>
<xsl:apply-templates select="title"/>
<xsl:apply-templates select="artist"/>
</p>
</xsl:template>
<xsl:template match="title">
Title: <span style="color:#ff0000">
<xsl:value-of select="."/></span>
<br />
</xsl:template>
<xsl:template match="artist">
Artist: <span style="color:#00ff00">
<xsl:value-of select="."/></span>
<br />
</xsl:template>
</xsl:stylesheet>
This was taken from http://www.w3schools.com/xsl/el_template.asp

how to call a template to edit a tei stylesheet

I'm trying to automatically make epubs with a list of names after the body section.. In order to do this, I'm changing the tei stylesheet.
First, I tryed to insert this code in the "to.xsl" file, inside the "profiles/default/epub" folder:
<xsl:template match="tei:body">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
<xsl:element name="back" namespace="http://www.tei-c.org/ns/1.0">
<xsl:element name="div" namespace="http://www.tei-c.org/ns/1.0">
<xsl:for-each select="//tei:rs[#type='luogo']">
<xsl:element name="p" namespace="http://www.tei-c.org/ns/1.0">
<xsl:value-of select="."/>
</xsl:element>
</xsl:for-each>
</xsl:element>
</xsl:element>
</xsl:template>
In this case the output shows the list of names before the body section.
Then I found the "bodyHook" template that you can see here, but it doesn't work (or I don't understand how to use it).
I tried to write something like this:
<xsl:param name="indiceNomi">
<back>
<div>
<xsl:for-each select="//tei:rs[#type='luogo']">
<p>
<xsl:value-of select="."/>
</p>
</xsl:for-each>
</div>
</back>
</xsl:param>
<xsl:template match="tei:body">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
<xsl:call-template name="bodyHook"/>
<xsl:with-param name="creaIndice" select="$indiceNomi"/>
</xsl:template>
But it is incorrect (seems that xsl:with-param can't be inside xsl:template, even if I saw some example like this).
So, if this is my input file, what kind of code I have to write?
<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" href="file:/C:/Users/User/Desktop/prova2.xsl"?>
<TEI xmlns="http://www.tei-c.org/ns/1.0">
<teiHeader><fileDesc>
<titleStmt>
<title>AA</title>
</titleStmt>
<publicationStmt><p><!-- supply publication information --></p></publicationStmt>
<sourceDesc>
<bibl>AA</bibl>
</sourceDesc>
</fileDesc><profileDesc>
<langUsage>
<language ident="ita">AA</language>
<language ident="lat">AA</language>
</langUsage>
</profileDesc></teiHeader>
<text>
<body>
<div type="book" n="3" xml:id="L3">
<head>AA
</head>
<div type="capitolo" n="1" xml:id="L3-01">
<head>AA</head>
<p>AA
<pb n="200"/>textt<rs type="luogo">Genova</rs>texttex ttexttexttexttexttexttexttexttext<rs type="luogo">London</rs>exttextte<rs type="luogo">Paris</rs>
texttexttexttexttexttexttexttexttext<rs type="luogo">Tokyo</rs>xttexttexttexttexttexttexttext<rs type="luogo">New York</rs>
<rs type="luogo">Dublin</rs><rs type="luogo">Moscow</rs><rs type="luogo">Cairo</rs>texttexttexttexttexttexttexttexttexttexttexttexttexttexttext</p>
</div>
</div>
</body>
</text>
</TEI>
Thanks in advance, it will help me a lot to have some answer.
I think you've misunderstood somewhat how to customize the TEI XSLT behavior. You're not supposed to modify the predefined files. The top of this page gives a simple explanation. Basically the idea is that you would create your own XSLT file that imports the tei.xsl file, and add your own templates to your own XSLT file to customize the behavior. For example, to define a template to be inserted at the end of the <body> section, you would add something like this to your XSLT file:
<xsl:template name="bodyEndHook">
<back>
<div>
<xsl:for-each select="//tei:rs[#type='luogo']">
<p>
<xsl:value-of select="."/>
</p>
</xsl:for-each>
</div>
</back>
</xsl:template>
Presumably, the TEI transform automatically calls this template at the end of the <body>. The default behavior for bodyEndHook is to do nothing, but you can override that default behavior by adding your own templat.

getting the value of the previous elements in other template in xslt

Source:
<Data>
<AB>
<choice>Disclose</choice>
<image>
<img alt="No Image" xlink:href="abcd:202-11587" xmlns="http://www.w3.org/1999/xhtml" xmlns:xlink="http://www.w3.org/1999/xlink" xlink:title="Image" />
</image>
<link>abcd</link>
</AB>
<AB>
<choice>All</choice>
<image>
<img alt="No Image" xlink:href="abcd:202-2202" xmlns="http://www.w3.org/1999/xhtml" xmlns:xlink="http://www.w3.org/1999/xlink" xlink:title="Image" />
</image>
<link>all</link>
</AB>
</Data>
XSLT
<xsl:template match="Data">
<xsl:for-each select="AB">
<xsl:variable name="temp" select="choice"/>
<xsl:choose>
<xsl:when test="$temp='Disclose'">
<xsl:apply-templates select="image/node()"/>
</xsl:when>
</xsl:choose>
</xsl:for-each>
</xsl:template>
<xsl:template match="simple:image/xhtml:img">
<!-- I want to get the the name of the "choice" here-->
<!-- some other process-->
<!-- how to access the value of the <choice> element of that section-->
<!-- how to access <link> element of that section-->
</xsl:template>
Can any one help how to do it.
Firstly, as this may just be an oversight with your code sample, you have specified namespaces in your matching template
<xsl:template match="simple:image/xhtml:img">
However, there are no references to the "simple" namespace in your sample XML, so in this case it should just be the following
<xsl:template match="image/xhtml:img">
But in answer to you question, to get the choice element, because you currently posisioned on the img element, you can search back up the hierarchy, like so
<xsl:value-of select="../../choice" />
The '..' represents the parent element. So, you are going back up to the AB element, and getting its child choice element.
And similarly for the link element
<xsl:value-of select="../../link" />
Note, it doesn't have to be xsl:value-of here, if there were multiple link elements, you could use xsl:apply-templates
<xsl:apply-templates select="../../link" />
And, if you required only link elements that occurred after the parent image element, you could do something like this
<xsl:apply-templates select="../following-sibling::link" />