getting the value of the previous elements in other template in xslt - 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" />

Related

xslt fields grouped by their type

I have some data that i put in (image, title, text). I ahve 3 different elements in my xslt for each of them:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:udt="DotNetNuke/UserDefinedTable" exclude-result-prefixes="udt">
<xsl:output method="xml" indent="yes" omit-xml-declaration="yes" />
<!--
This prefix is used to generate module specific query strings
Each querystring or form value that starts with udt_{ModuleId}_param
will be added as parameter starting with param
-->
<xsl:variable name="prefix_param">udt_<xsl:value-of select="//udt:Context/udt:ModuleId" />_param</xsl:variable>
<xsl:template match="udt:Data" mode="list">
<xsl:value-of select="udt:Image" disable-output-escaping="yes" />
<xsl:value-of select="udt:Title" disable-output-escaping="yes" />
<xsl:value-of select="udt:Text" disable-output-escaping="yes" />
</xsl:template>
<xsl:template match="/udt:UserDefinedTable">
<xsl:variable name="currentData" select="udt:Data" />
<xsl:if test="$currentData">
<xsl:apply-templates select="$currentData" mode="list">
</xsl:apply-templates>
</xsl:if>
</xsl:template>
<xsl:template name="EditLink">
<xsl:if test="udt:EditLink">
<a href="{udt:EditLink}">
<img border="0" alt="edit" src="{//udt:Context/udt:ApplicationPath}/images/edit.gif" />
</a>
</xsl:if>
</xsl:template>
</xsl:stylesheet>
I want to fill these elements each into its own div group, so my end result would be something like this:
<div id="images">
<!--all images here-->
</div>
<div id="title">
<!--all titles here-->
</div>
<div id="text">
<!--all texts here-->
</div>
Can this be achieved by any kind of grouping or whats the right aproach?
The concept to use xsl:value-of for your 3 element types is wrong,
as this instruction copies only the content of these elements,
without XML markup.
Assuming that the only goal is to reorder (group) your elements,
and they are direct descendants of the current element (udt:Data),
the task can be done the following way:
<xsl:template match="udt:Data">
<xsl:copy>
<div id="images">
<xsl:copy-of select="udt:Image"/>
</div>
<div id="title">
<xsl:copy-of select="udt:Title"/>
</div>
<div id="text">
<xsl:copy-of select="udt:Text"/>
</div>
</xsl:copy>
</xsl:template>
Of course, this is only a template, not the whole script.
Note that e.g. if these elements were located also at "deeper" descendant levels,
all the above XPath expressions should be prececed with descendant::.
And remember about including in your script all namespaces, the script refers to.
They should be declared in stylesheet tag.

applying templates of same type but working on different type of declaration

I've below XMLs.
<emphasis type="italic">
varying from ti,e to time<star.page>58</star.page>Starch
</emphasis>
and
<para indent="no">
<star.page>18</star.page> Further to same.
</para>
here i'm trying to apply-templates on the star.page, but the confusion is if i take <xsl:apply-templates select="./*[1][self::star.page]" mode="first"/>, it is working fine for the first case, but for the second case, the star.page is getting duplicated, if i use <xsl:apply-templates select="./node()[1][self::star.page]" mode="first"/>, in case 2 the star.page that is supposed to appear before div is coming inside div and for case 1, the value is getting duplicated.
Here are the DEmos
Case1-enter link description here
Case2- enter link description here
Expected output are as below.
Case 1:
<span class="font-style-italic">
varying from ti,e to time<?pb label='58'?><a name="pg_58"></a></span>2<span class="font-style-italic">Starch
</span>
Case 2:
<?pb label='18'?><a name="pg_18"></a>
<div class="para">
Further to same.
</div>
Here the condition is as below.
If star.page is immediate child of parent node(though it is para or emphasis), the pb label has to be created first followed by the tag(Case 2 output).
If there is text and in between text there is star.page, then the content should come with pb label inside it.(Case 1 output).
please let me know a common solution on how i can fix theses issues.
I am not clear what the bulk of your XSLT is doing, but I would first make use of a named template to avoid repeated code. There is no issue with giving a matched template a name too.
<xsl:template match="star.page" name="page">
<xsl:processing-instruction name="pb">
<xsl:text>label='</xsl:text>
<xsl:value-of select="."/>
<xsl:text>'</xsl:text>
<xsl:text>?</xsl:text>
</xsl:processing-instruction>
<a name="{concat('pg_',.)}"/>
</xsl:template>
Then the template with the mode "first" becomes this
<xsl:template match="star.page" mode="first">
<xsl:call-template name="page" />
</xsl:template>
You can still call this in the same way, or maybe a slightly different condition will also work
<xsl:apply-templates select="star.page[not(preceding-sibling::node())]" mode="first"/>
Then, all you need is a template to ignore "star.page" that are the first child (because they have already been explicitly selected)
<xsl:template match="star.page[not(preceding-sibling::node())]" />
As a simplified example, try this for starers
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output method="xml" indent="yes" />
<xsl:strip-space elements="*" />
<xsl:template match="para|emphasis">
<xsl:apply-templates select="star.page[not(preceding-sibling::node())]" mode="first"/>
<div>
<xsl:choose>
<xsl:when test="./#align">
<xsl:attribute name="class"><xsl:text>para align-</xsl:text><xsl:value-of select="./#align"/></xsl:attribute>
</xsl:when>
<xsl:otherwise>
<xsl:attribute name="class"><xsl:text>para</xsl:text></xsl:attribute>
</xsl:otherwise>
</xsl:choose>
<xsl:apply-templates/>
</div>
</xsl:template>
<xsl:template match="star.page[not(preceding-sibling::node())]" />
<xsl:template match="star.page" name="page">
<xsl:processing-instruction name="pb">
<xsl:text>label='</xsl:text>
<xsl:value-of select="."/>
<xsl:text>'</xsl:text>
<xsl:text>?</xsl:text>
</xsl:processing-instruction>
<a name="{concat('pg_',.)}"/>
</xsl:template>
<xsl:template match="star.page" mode="first">
<xsl:call-template name="page" />
</xsl:template>
</xsl:stylesheet>
Do note the use of strip-space here because strictly speaking, the star.page is not the first node in each example, there is a white-space node before it.
When applied to this XML
<root>
<emphasis type="italic">
varying from ti,e to time<star.page>58</star.page>Starch
</emphasis>
<para indent="no">
<star.page>18</star.page> Further to same.
</para>
</root>
The following is output
<div class="para">
varying from time to time<?pb label='58'??><a name="pg_58"/>Starch
</div>
<?pb label='18'??>
<a name="pg_18"/>
<div class="para"> Further to same.
</div>

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

Force check of each entry when applying template?

I have a template which fetches images and then outputs them into one of two templates.
I would like it to track each individual image and output based on the values of each one. Currently, if one image is wide, it outputs them all according to the wide template. I would rather utilize both templates.
<xsl:template name="get-images">
<xsl:param name="image-entry"/>
<xsl:choose>
<xsl:when test="($image-entry/image/meta/#width) > ($image-entry/image/meta/#height)">
<xsl:apply-templates select="$image-entry/image" mode="wide">
<xsl:with-param name="image-class" select="'full-width'"/>
<xsl:with-param name="caption-class" select="'full-width-caption'"/>
</xsl:apply-templates>
</xsl:when>
<xsl:otherwise>
<xsl:apply-templates select="$image-entry/image" mode="tall">
<xsl:with-param name="image-class" select="'center'"/>
<xsl:with-param name="caption-class" select="'full-width-caption'"/>
</xsl:apply-templates>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template match="image" mode="wide">
<xsl:param name="image-class" />
<xsl:param name="caption-class" />
<a href="{$root}/image/full{#path}/{filename}">
<img src="{$root}/image/wide{#path}/{filename}" alt="{../description}" class="{$image-class}"/>
</a>
<p class="{$caption-class}">
Image courtesy of: <xsl:value-of select="../title"/>
</p>
</xsl:template>
<xsl:template match="image" mode="tall">
<xsl:param name="image-class" />
<xsl:param name="caption-class" />
<span class="centered">
<a href="{$root}/image/full{#path}/{filename}">
<img src="{$root}/image/tall{#path}/{filename}" alt="{../description}" class="{$image-class}"/>
</a>
</span>
<p class="{$caption-class}">
Image courtesy of: <xsl:value-of select="../title"/>
</p>
</xsl:template>
Bonus question: How can I ignore the caption if value source doesn't exist?
<article-images field-id="24" subsection-id="5" items="1">
<item id="109" creation-date="2014-04-24T05:16:52+01:00">
<image size="317 KB" path="/uploads" type="image/jpeg">
<filename>funny-lolcat.jpg</filename>
<meta creation="2014-04-24T05:16:52+01:00" width="1600" height="1200" />
</image>
<description mode="formatted"><p>Aww!</p>
</description>
<title handle="" />
<source handle="" />
</item>
</article-images>
Instead of two modes, use one mode and move the wide vs not-wide logic into the template match expressions:
<xsl:template match="image[meta/#width > meta/#height]">
<!-- logic for wide image -->
</xsl:template>
<xsl:template match="image">
<!-- logic for not-wide image -->
</xsl:template>
And now you can just apply templates to all the images in one go without the choose:
<xsl:apply-templates select="$image-entry/image"/>
To ignore the caption if there's no source, I'd move the caption logic into another template matching the source element
<xsl:template match="source" mode="caption">
<p class="full-width-caption">
Image courtesy of: <xsl:value-of select="../title"/>
</p>
</xsl:template>
Then in the main template do:
<xsl:apply-templates select="../source" mode="caption"/>
If there is a source this will produce a caption, if there isn't then it'll produce nothing.
Given the example you've just added to the question it looks like you want to exclude the caption no when the source element "doesn't exist" but rather if it has no value. You can do this by changing the above apply-templates to
<xsl:apply-templates select="../source[string()]" mode="caption" />
This would add a caption for <source handle="">something</source> but not for <source handle="" />.
What this is doing is filtering so we only select the ../source element if the [string()] predicate is true. The string() function returns the "string value" of the context element (the source in this case) and a string in boolean context is treated as false if it is empty and true otherwise. So in this case the effect is to apply templates to the source element only if it has a non-empty value.

XSLT matching PAGEID to an element ID

How would I match two separate numbers in an XML document? There are multiple <PgIndexElementInfo> elements in my XML document, each representing a different navigation element, each with a unique <ID>. Later in the document a <PageID> specifies a number that sometimes matches an <ID> used above. How could I go about matching the <PageID> to the <ID> specified above?
<Element>
<Content>
<PgIndexElementInfo>
<ElementData>
<Items>
<PgIndexElementItem>
<ID>1455917</ID>
</PgIndexElementItem>
</Items>
</ElementData>
</PgIndexElementInfo>
</Content>
</Element>
<Element>
<Content>
<CustomElementInfo>
<PageID>1455917</PageID>
</CustomElementInfo>
</Content>
</Element>
EDIT:
I added the solution below to my code. The xsl:apply-templates that is present is used to recreate the nested lists that are lost between HTML and XML. What I now need to do is match the PageID to the ID of a <PgIndexElementItem> and add a CSS class to the <ul> it is a part of. I hope that makes sense.
<xsl:key name="kIDByValue" match="ID" use="."/>
<xsl:template match="PageID[key('kIDByValue',.)]">
<xsl:apply-templates select="//PgIndexElementItem[not(contains(Description, '.'))]" />
</xsl:template>
<xsl:template match="PgIndexElementItem">
<li>
<xsl:value-of select="Title"/>
<xsl:variable name="prefix" select="concat(Description, '.')"/>
<xsl:variable name="childOptions"
select="../PgIndexElementItem[starts-with(Description, $prefix)
and not(contains(substring-after(Description, $prefix), '.'))]"/>
<xsl:if test="$childOptions">
<ul>
<xsl:apply-templates select="$childOptions" />
</ul>
</xsl:if>
</li>
</xsl:template>
The XSLT way for dealing with cross references is with keys.
Matching: A rule matching every PageID element that it has been referenced by an ID element.
<xsl:key name="kIDByValue" match="ID" use="."/>
<xsl:template match="PageID[key('kIDByValue',.)]">
<!-- Template content -->
</xsl:template>
Selecting: A expression selecting every PageID element with specific value.
<xsl:key name="kPageIDByValue" match="PageID" use="."/>
<xsl:template match="ID">
<xsl:apply-templates select="key('kPageIDByValue',.)"/>
</xsl:template>