XSL: Calling specific Element from Multiple Instances of one Schema - xslt

I created a component named "BusLocationLinks" it stores the name of the business as well as the coordinates for a map I created.
I have nearly 50 business locations with that same schema (BusLocationsLinks) and only want to list the element "Business Name" of all components components of that name. I've tried everything but can not make them all display. ANY ADVICE?
Here's my current code:
<xsl:template name="BusLocationLinks">
<xsl:for-each select="BusLocationLinks/BusinessName">
<li class="active">
<xsl:value-of select="BusinessName" />
</li>
</xsl:for-each>
</xsl:template>
my xml code looks similar to such:
<BusLocationLinks>
<BusinessName>Star Property</BusinessName>
</BusLocationLinks>

Without seeing your XML it is difficult to diagnose the problem. However, it is likely that you have the following structure:
<BusLocationLinks>
<BusinessName>name1</BusinessName>
<BusinessName>name2</BusinessName>
<BusinessName>name3</BusinessName>
</BusLocationLinks>
If that is the case, then you should adjust your XSLT like this:
<xsl:template name="BusLocationLinks">
<xsl:for-each select="BusinessName">
<li class="active">
<xsl:value-of select="." />
</li>
</xsl:for-each>
</xsl:template>

The body of an xsl:for-each instruction resets the context node to be one of the nodes in the selected node set (a different one each time the body of the for-each is evaluated).
In your example, that means that within the body of the for-each, the current node is one of the BusLocationLinks/BusinessName elements you selected. Your loop creates a list-item element for each of them (check your output, I expect you'll see them there) containing the value of the BusinessName child of the context node. The context node matches the expression BusLocationLinks/BusinessName, so you are looking for the values of nodes which match BusLocationLinks / BusinessName / BusinessName. If you don't have any nodes that match the expression BusLocationLinks / BusinessName / BusinessName, you'll be getting empty li elements.
Try <xsl:value-of select="."/>.

Related

How to apply-templates to all templates except a spefic one

In the code bellow I'm applying all the templates to the element chapter (in the end of the code) but I would like to know how it is possible to apply all the templates to this element except a spefic one. In this case it's the element title because I'm already selecting it in the line before and it appears repeated in the html file. Someone?
<xsl:template match="chapter">
<h3>
<a name="{#id}"><xsl:value-of select="title"/></a>
</h3>
<xsl:apply-templates/>
</xsl:template>
Output:
<h3>Title</h3>
Title<br>
Text.
A plain <xsl:apply-templates/> is equivalent to <xsl:apply-templates select="node()" />, i.e. all child nodes. You can exclude certain nodes by using the XPath 2.0 except operator, e.g.
<xsl:apply-templates select="node() except title" />
This would select all child nodes except those that are elements with the name title. If you are only interested in child elements (not text nodes etc.) then you could use * except title instead.
The except operator essentially implements set difference - you're not limited to simple element names on the right, you can use any expression that returns a sequence of nodes, e.g.
node() except (title | div[#class = 'heading'])
X except Y selects all nodes that are in the sequence selected by X and not also in the sequence selected by Y

Change text of elements identified by dynamic XPath

I have an XML with 2 XML fragments, 1st one is a fragment where the new values must be applied (which can have pretty complex elements) like
... some static parents
<a:element1>
<a:subelement tag="someString">
<a:s1>a</a:s1>
</a:subelement>
</a:element1>
<a:element2>b</a:element2>
<a:element3>c</a:element3>
... lots of other elements like the above ones
and 2nd fragment that has XPaths generated from the first XML and a new value, like
<field>
<xpath>/Parent/element1/subelement[#tag="someString"]/s1</xpath>
<newValue>1</newValue>
</field>
<field>
<xpath>/Parent/element2</xpath>
<newValue>2</newValue>
</field>
We might not have new values to apply for all the elements in the first fragment.
I'm struggling to make an XSLT transformation that should apply the new values to the places indicated by the XPaths.
The output should be:
... some static parents
<a:element1>
<a:subelement tag="someString">
<a:s1>1</a:s1>
</a:subelement>
</a:element1>
<a:element2>2</a:element2>
... lots of other elements like the above ones
I have access to xalan:evaluate to evaluate the dynamic xpath. I'm trying different solutions, I will write them here when they will start to make sense.
Any ideas of approaches are well received. Thanks
Oki, I found out how, and I will write the answer here maybe someone sometime will need this:
<xsl:template match="/">
<!-- static parents -->
<a:Root>
<xsl:apply-templates select="/a:Root/a:Parent" />
</a:Root>
</xsl:template>
<xsl:template match="#*|*|text()">
<xsl:variable name="x" select="generate-id(../.)" />
<xsl:variable name="y" select="//field[generate-id(xalan:evaluate(xpath)) = $x]" />
<xsl:choose>
<xsl:when test="$y">
<xsl:value-of select="$y/newValue" />
</xsl:when>
<xsl:otherwise>
<xsl:copy>
<xsl:apply-templates select="#*|*|text()" />
</xsl:copy>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
And to explain the transformation:
I'm writing down part that is static and then call apply-templates on the fragment I'm interested in, that has a liquid structure.
Then I'm using a slightly modified identity transformation that copies everything from source to target (starting from the /a:Root/a:Parent fragment), except when we position ourselves on the text I'm interested in changing.
The text() I'm interested in will have as parent (../.) the element referred by an xpath string found in the second fragment. Variable x means, in the context of the when, this element.
Variable y finds a field element that has as child an xpath element that if evaluated using xalan will refer to the same element that the x variable relates to.
Now I used generate-id() in order to compare the physical elements, otherwise it would have compared by the toString of the element (which is wrong). If variable y doesn't exist, it means that I have no xpath element for this element that could have changed, and I'm leaving it alone. If the y variable exists, I can get from it the newValue and I'm currently positioned on the element which text I want to update.

What does this do? <xsl:apply-templates select="."/> and <xsl:apply-templates select="*|#*"/>

I am very new to XSL and I am confused about what the select inside the following pieces of code will select.
<xsl:apply-templates select="."/>
<xsl:apply-templates select="*|#*"/>
Any ideas?
Thanks
Check out the Abbreviated Syntax section of XPath 2.0.
In the <xsl:apply-templates select="."/> example, . evaluates to the context item. In most cases, this is the same as the node currently being processed. So this example will select the context node.
In the <xsl:apply-templates select="*|#*"/> example, * will select all the child elements of the context node. #* will select all the attributes of the context node. | is the union operator. So this example will select all the child elements of the context node and also all the attributes of the context node.
<xsl:apply-templates select="."/> is frequently used to apply further processing to the context node.
<xsl:apply-templates select="*|#*"/> is frequently used to process all the child elements of the current node and its attributes. It’s often used when you’re done handling an element and want to hand off its child elements/attributes to any other templates that apply.
<xsl:apply-templates select="."/>
processes the content of the current node! the dot . indicates content.. if the current node doesn't have childnodes but has data instead (ex: <foo>Sample Data</foo>) then the parser processes the data Sample Data
<xsl:apply-templates select="#*|*"/>
processes the attribute and child nodes or the data under the current node.. difference is .. this one takes care of all the attribute of the context node..
I use the word process instead of copy, because .. apply-template unlike copy-of and value-of evaluates other templates, for example along with above code if I have one more template like below:
<xsl:template match="text()[.='Sample Data']"/>
then it will drop the text from your output XML. Where as copy-of select="node_name" and value-of select="node-name" copy the data despite of being this template in our XSL file..

convert <xsl:variable> string value , to the value which I can select with <xsl:value-of select>

I have a variable from which I need to dynamically generate nodes
<xsl:template match="banner_discount_1 | banner_discount_2 | banner_discount_3">
<xsl:variable name="link">banner_discount_<xsl:value-of select="substring-after(name(.) ,'banner_discount_')" />_link</xsl:variable>
<xsl:value-of select="$link" />
</xsl:template>
<xsl:value-of> selects the string, but I want to be able to select the node which name matches the name of a variable.
In my case the node looks something like this:
<banner_discount_1_link />
<banner_discount_2_link />
...
Here is the xml I'm using
<banner_discount_1> 12 </banner_discount_1>
<banner_discount_2> 21 </banner_discount_2>
<banner_discount_3> 32 </banner_discount_3>
<banner_discount_1_link> link1 </banner_discount_1_link>
<banner_discount_2_link> link2 </banner_discount_2_link>
<banner_discount_3_link> link3 </banner_discount_3_link>
#MartinHonnen is on the right track, but you need to set the selection context as well.
Since you're in a template that's selecting the banner_discount_ nodes, that is your context. From your XML sample, it looks like the nodes you want to select are siblings, so this should work:
<xsl:value-of select="../*[local-name() = $link]"/>
It is preferable to target the nodes directly, but if they could be anywhere in the document, then you may resort to
<xsl:value-of select="//*[local-name() = $link]"/>
This is a last resort because it is potentially O(n) with respect to the number of nodes in the document.
Use <xsl:value-of select="*[local-name() = $link]"/>. If that does not help then consider to show a sample of the XML.

Creating a variable number of nodes on target document without corresponding data on source document

I'm trying to map two documents witht the BizTalk Mapper and my target document should look like this:
<root>
<complexType>
<property>example</property>
</complexType>
<filler>
<padding>9999999</padding>
</filler>
<filler>
<padding>9999999</padding>
</filler>
<filler>
<padding>9999999</padding>
</filler>
</root>
The number of <filler> nodes that I should create is variable (from 0 to 9). It is basically the result of a calculation (based on some data provided in the source document).
Is there a way to create those <filler> nodes with some combination of functoids?
I tried to use the Table Looping functoid (created a table with only one column, the padding char '9') but doesn't really work because it creates as many <filler> nodes as rows are defined in the table, which is not what I want since the number of rows would have to be variable (again, based on a calculation).
What I currently do is pass the message (XmlDocument) to a C# method and then I programmatically append the <filler> nodes.
I'm hoping that there is a more "BizTalk-y" way of doing this with the Mapper.
I suspect that you will have to solve this problem by altering the XSLT.
Add some logic to create as many filler nodes as the result of your calculation dictates - you could create a template which you call in a loop perhaps, which would append a new filler section.
Hope this points you in the right direction.
As pointed out, XSLT can create nodes on the target document at will (I didn't know this and this was the key part).
Turns out that what I needed is a simple for-loop in XSLT. Once I realized this, a quick Google search yielded the following results:
http://quomon.com/question-How-to-make-a-for-loop-in-xslt-not-for-each-809.aspx
http://snippets.dzone.com/posts/show/930
Another thing worth noting is that (as pointed out by the first link), XSLT is a functional language, not procedural, so sometimes you do have to resort to using recursion or an extension.
This case is definitely one of those times since I couldn't use a careful selection of nodes using the select attribute on an xsl:for-each (since this filler data wasn't part of the source document).
Specifically, for this case, what I did was:
Add a Scripting functoid.
Add two inputs:
A constant with value "1" (this is the initial value of the i variable)
The length of the loop (number of times to repeat the body of the loop)
Paste the following XSLT template as an "Inline XSLT Call Template" script:
<xsl:template name="ForLoop">
<xsl:param name="i" /> <!-- index counter, 1-based, will be incremented with every recursive call -->
<xsl:param name="length" /> <!-- exit loop when i >= length -->
<!-- Output the desired node(s) if we're still looping -->
<!-- The base case is when i > length (in that case, do nothing) -->
<xsl:if test="$i <= $length">
<Filler>
<Padding>999999</Padding>
</Filler>
</xsl:if>
<!-- Call the ForLoop template recursively, incrementing i -->
<xsl:if test="$i <= $length">
<xsl:call-template name="ForLoop">
<xsl:with-param name="i">
<xsl:value-of select="$i + 1"/>
</xsl:with-param>
<xsl:with-param name="length">
<xsl:value-of select="$length"/>
</xsl:with-param>
</xsl:call-template>
</xsl:if>
</xsl:template>