XSL variable number of child nodes - xslt

I've got some XML elements with a number attached as more are available.
Such as this:
<Images>
<Image1>C:\Path\To\AnImage</Image1>
<Image2>C:\Path\To\AnotherImage</Image2>
</Images>
The amount of Images in each XML doc is variable. How can I make sure that my XSL file will show all elements inside the tag?
I also want to put each of the strings inside each ImageX tag inside a Img src="stringfromxmlelement" with XSL? Is this possible?
Tony

<xsl:template match="Images/*[starts-with(name(),'Image']">
<img src="{.}" />
</xsl:template>
BTW, perhaps you can't change the XML tag names, but it would better to name the inner tags as Image rather than ImageX, which is probably unneccesary.

I'd try something along these lines:
<xsl:template match="Images">
<xsl:for-each select="*">
<img src="{text()}" />
</xsl:for-each>
</xsl:template>

Related

How to make XSLT xsl:if work

I am trying to make this (xml 1.0) code work . I am new to this and already exhausted myself in
trying different ways. Does someone know my mistake?
<xsl:for-each select="News/Sport">
<xsl:if test="local-name()='Basketball'">
<p>
<xsl:text>Basketball Sport</xsl:text>
</p>
<xsl:value-of select="News/Sport/Basketball/Phrases/Phrase"/>
</xsl:if>
</xsl:for-each>
When I transform it into an HTML file the content doesn't show up. When I remove the xsl:for each and the xsl:if statements the content is successfully presented. I only wish that the content is first checked (if it is available in the XML file) and if yes, that it is taken from the XML content.
Thank you in advance for your help!
EDIT:
This is my XML code
<News>
<Sport>
<Basketball>
<Phrases>
<Phrase>Zach Randolph recovered the opening tipoff in Game 1 of the Western Conference Finals, and he didn’t touch the ball again until the possession following the Grizzlies’ first timeout.
</Phrase>
<Phrases>
</Basketball>
</Sport>
</News>
EDIT2:
Could you tell me why I cannot apply a template inside this below function? Only the text works now:(
<xsl:for-each select="News/Sport[Basketball]">
<xsl:apply-templates select="News/Sport/*" />
</xsl:for-each>
<xsl:template match="Basketball">
<p>
<xsl:text>Basketball Sport</xsl:text>
</p>
<xsl:apply-templates select="Phrases/Phrase"/>
</xsl:template>
<xsl:for-each select="News/Sport">
<xsl:if test="local-name()='Basketball'">
In this if test, the context node is a Sport element, so local-name() will always be Sport and will never equal Basketball.
I only wish that the content is first checked (if it is available in the XML file) and if yes, that it is taken from the XML content.
The usual way to handle this sort of thing in XSLT is to define templates matching the various nodes that might be present and then applying templates to all the nodes that are actually found. If there are no nodes of a particular type then the corresponding template will not fire
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output method="html" />
<xsl:template match="/">
<html>
<body>
<!-- apply templates that match all elements inside Sport, which may
be Basketball, Football, etc. -->
<xsl:apply-templates select="News/Sport/*" />
</body>
</html>
</xsl:template>
<!-- when we find a Basketball element ... -->
<xsl:template match="Basketball">
<p>
<xsl:text>Basketball Sport</xsl:text>
</p>
<xsl:apply-templates select="Phrases/Phrase"/>
</xsl:template>
<!-- when we find a Football element ... -->
<xsl:template match="Football">
<p>
<xsl:text>Football Sport</xsl:text>
</p>
<!-- whatever you need to do for Football elements -->
</xsl:template>
<xsl:template match="Phrase">
<p><xsl:value-of select="." /></p>
</xsl:template>
</xsl:stylesheet>
That way you don't need any explicit for-each or if, the template matching logic handles it all for you.
You are missing the idea of a context node. Within a xsl:for-each, everything you refer to is about or relative to the selected nodes. So, for instance, within <xsl:for-each select="News/Sport">, the context node is the Sport elements, and a test like <xsl:if test="local-name()='Basketball'"> is always going to be false because the local name is always Sport.
It looks like you want just <xsl:if test="Basketball"> which tests whether there are any Basketball child nodes of the current Sport node.
The same thing applies to <xsl:value-of select="News/Sport/Basketball/Phrases/Phrase"/>. Because everything is relative to the Sport node, XSLT is looking for News/Sport/Basketball/Phrases/Phrase within the Sport element, which never exists.
In addition, you can just put text in literally: there is no need for an xsl:text element here, so your code should look like
<xsl:for-each select="News/Sport">
<xsl:if test="Basketball">
<p>Basketball Sport</p>
<xsl:value-of select="Basketball/Phrases/Phrase"/>
</xsl:if>
</xsl:for-each>
You can refine this further by adding a predicate to the for-each selection, so that it matches only Sport elements with a Basketball child. Like this
<xsl:for-each select="News/Sport[Basketball]">
<p>Basketball Sport</p>
<xsl:value-of select="Basketball/Phrases/Phrase"/>
</xsl:for-each>

XSLT for-each, iterating through text and nodes

I'm still learning XSLT, and have a question about the for-each loop.
Here's what I have as far as XML
<body>Here is a great URL<link>http://www.somesite.com</link>Some More Text</body>
What I'd like is if the for-each loop iterate through these chunks
1. Here is a great URL
2. http://www.somesite.com
3. Some More Text
This might be simple, or impossible, but if anyone can help me out I'd appreciate it!
Thanks,
Michael
You should be able to do so with something like the following:
<xsl:for-each select=".//text()">
<!-- . will have the value of each chunk of text. -->
<someText>
<xsl:value-of select="." />
</someText>
</xsl:for-each>
or this may be preferable because it allows you to have a single template that you can invoke from multiple different places:
<xsl:apply-templates select=".//text()" mode="processText" />
<xsl:template match="text()" mode="processText">
<!-- . will have the value of each chunk of text. -->
<someText>
<xsl:value-of select="." />
</someText>
</xsl:for-each>

How to prevent self closing tags as well empty tags after transforming

I have in an input file:
<a></a>
<b/>
<c>text</c>
I need to converting this to string. Using transformer I am getting below output:
<a/> <!-- Empty tags should not collapse-->
<b/>
<c>text</c>
If I use xslt and output method is "HTML", I get the below output:
<a></a> <!-- This is as expected-->
<b></b> <!-- This is not expected-->
<c>text</c>
I want the structure same as in input file. It is required in my application since I need to calculate index and it will be very difficult to change the index calution logic.
What would be the correct XSLT to use?
What XSLT processor? XSLT is merely a language to transform xml so "html output" is dependent on the processor.
I'm going to guess this first solution is too simple for you but i've had to use this to avoid processing raw html
<xsl:copy-of select="child::node()" />
as this should clone the raw input.
In my case, I have used the following to extract all nodes that had the raw attribute:
<xsl:for-each select="xmlData//node()[#raw]">
<xsl:copy-of select="child::node()" />
</xsl:for-each>
Other options:
2) Add an attribute to each empty node depending on what you want it to do later ie role="long", role="short-hand".
3)
Loop through each node (xsl:for-each)
<xsl:choose>
<xsl:when test="string-length(.)=0"> <!-- There is no child-->
<xsl:copy-of select="node()" />
</xsl:when>
<xsl:otherwise>
...whatever normal processing you have
</xsl:otherwise>
4) Redefine your problem. Both are valid XHTML/XML, so perhaps your problem can be reframed or fixed elsewhere.
Either way, you may want to add more information in your question so that we can reproduce your problem and test it locally.
P.S. Too much text/code to put in a comment, but that's where this would belong.
A possible alternative is to use disable-output-escaping like this:
<xsl:text disable-output-escaping="yes"><a></a></xsl:text>
But I understand that this is a dirty solution...

display xml elements inside xml using xslt

Ia using XSLT 1.0 and I do have a XML while looks like this
<item name="note"><value><p>Add the &lt;bean&gt; tag pased below to the &lt;beans&gt; element in this file .... </value></item>
I want to display it like this in HTML
Add the <bean> tag passed below to the <beans> element in this file.
Note here that the <p> will be converted to a paragraph tag as I use disable-output-escaping= yes.
This is what I have in my xslt
<xsl:template match="item[#name='note']">
<xsl:value-of select="value" disable-output-escaping="yes" />
</xsl:template>
With this xslt it ignores the bean and beans xml and it does not get displayed in the page. How do I make sure to display it the way I want it?
The problem is that some of your entities have been "double escaped".
&lt;bean&gt; should be <bean>

How to access variable in CDATA from XSLT?

I am using XSLT Transformation and need to put some data in CDATA section and that value is present in a variable.
Query: How to access variable in CDATA ?
Sample Given Below:
<xsl:attribute name ="attributeName">
<![CDATA[
I need to access some variable here like
*<xsl:value-of select ="$AnyVarible"/>*
]]>
</xsl:attribute>
How can I use varibale in CDATA ?
Note: I can not use --> <![CDATA[<xsl:value-of select ="$AnyVarible"/>]]>
Thanks in advance.
I got the solution for this...FYI for everyone...
<xsl:text
disable-output-escaping="yes"><![CDATA[</xsl:text>
<xsl:value-of select ="$AnyVarible"/>
<xsl:text
disable-output-escaping="yes">]]></xsl:text>
CDATA is just text like any other element contents...
But using the xsl:output element you should be able to specify which elements are to be written as CDATA with the cdata-section-elements attribute.
EDIT:
Now that there is a valid sample, I guess you mean this:
<xsl:attribute name ="attributeName">
<![CDATA[
I need to access some variable here like
*]]><xsl:value-of select ="$AnyVarible"/><![CDATA[*
]]>
</xsl:attribute>
If you want to include CDATA sections in your output, you should use the cdata-section-elements atribute of xsl:output. This is a list of element names. Any such elements will have their text content wrapped in CDATA.
<xsl:output cdata-section-elements ="foo" />
<foo>
<xsl:value-of select="$bar' />
</foo>