When i take position in xslt ,the value is starting with 5. But it should be start with "1". Is there any reason i am getting value 5 here. I am using the variable "tempPosition" to get the value of position. Since it is starting with 5 i am subtracting 4 to start from 1. But i would like to know why it is starting from 5..
Here is my xslt:
<param name="Temp.response"/>
<template match="mm:value[position() <= 90]">
<variable name="tempPosition" select="(position()-4)"/>
<!-- Get node name of parent element -->
<variable name="mm" select="local-name(..)"/>
<Wed3Tierfff>
<variable name="tempStrtDt"
select="/mm:response/mm:data/mm:ffst/#starmmime"/>
<fffStartDt>
<value-of select="$tempStrtDt"/>
</fffStartDt>
<variable name="tempStrtDtHrs" select="substring($tempStrtDt,12,2)"/>
<variable name="tempStrtDtFinalHrs"
select="$tempPosition+$tempStrtDtHrs"/>
<fffEffectDtHe>
<choose>
<when test="string-length($tempStrtFinalHrs) = 1">
<variable name="tempfffEffectDtHe"
select="concat(substring($tempStrtDt,1,11),0,$tempStrtDtFinalHrs,substring($tempStrtDt,14,12))"/>
<value-of select="$tempfffEffectDtHe"/>
</when>
<otherwise>
<variable name="tempfffEffectDtHe"
select="concat(substring($tempStrtDt,1,11),$tempStrtDtFinalHrs,substring($tempStrtDt,14,12))"/>
<value-of select="$tempfffEffectDtHe"/>
</otherwise>
</choose>
</fffEffectDtHe>
</Wed3Tierfff>
</template>
<!-- Match the "ffst" node and create the "TierCollection" node. Apply templates to children of the "ffst" node -->
``
You should post a reproducible example, preferably minimized to only what's necessary to demonstrate the problem.
In general, the position() function returns the position of the current node in the current node list. And the current node list is determined by the currently instantiated xsl:apply-templates or xsl:for-each instruction's select expression - not by any template's match pattern.
It's not possible to tell what position() should be within your template without seeing the apply-templates instruction that invokes it. If your apply-templates instruction selects 17 nodes, and template T matches the fifth of those nodes, then within template T the value of position() will be 5. You haven't said why you think it should be one, so I suspect you havent' understood the spec correctly.
Related
I have the following XML structure:
<itemsWithLabels>
<itemLabelValue>
<label>A</label>
<value>a</value>
</itemLabelValue>
<itemLabelValue>
<label>B</label>
<value>b</value>
</itemLabelValue>
<itemLabelValue>
<label>C</label>
<value>c</value>
</itemLabelValue>
</itemsWithLabels>
Using XSL I want to be able to get the value from <value> by knowing the label in <label>.
So my transformation looks like this:
<xsl:value-of select="$content/itemsWithLabels/itemLabelValue/value[#label='A']" />
But clearly something is wrong because I don't have any output.
What am I doing wrong?
Try:
<xsl:value-of select="$content/itemsWithLabels/itemLabelValue[label='A']/value" />
$content/itemsWithLabels/itemLabelValue[label='A'] gets itemLabelValue elements with a label element child having value A. The /value part gets the child value element.
I want to get the value of a specific node, specified by its id attribute. But the behaviour of my XSL parser, Saxon, is not how I expected it to work.
This is my XSL code:
<xsl:template match="synonyme">
<xsl:element name="corr">
<xsl:value-of select="#connecteur" />
<xsl:value-of select="/liste/connecteur[#id=#connecteur]/forme[1]" />
</xsl:element>
</xsl:template>
I just matched a tag named synonyme which has a connecteur attribute. My code outputs the value of this attribute.
I also want to output the value of another node which has an id attribute matching the connecteur attribute of my currently matched synonyme tag. But no results are ever found for this query, the second value-of always has empty output.
If I write, e.g. [#id='c160'], where c160 is the exact same thing that is output by the first value-of, it works! But not when comparing to the #attribute of my matched node. How can I fix this?
The XML is basically
<liste><connecteur id="c160"><forme>foo</forme></connecteur>
<connecteur id="c161"><synonyme connecteur="c160" /></connecteur>
</liste>
and the expected output in place of the synonyme is <corr>c160 foo</corr>.
The predicate you use:
[#id=#connecteur]
is looking for an element with two attributes - id and connecteur- with equal values. To look for an element with an id attribute whose value matches the value of the current element's connecteur value, you need to use:
[#id=current()/#connecteur]
See: https://www.w3.org/TR/xslt/#function-current
A better solution would be to define a key as:
<xsl:key name="ref" match="connecteur" use="#id" />
then use:
<xsl:value-of select="key('ref', #connecteur)/forme" />
to resolve the cross-reference.
See: https://www.w3.org/TR/xslt/#key
I've got an xsl:variable that contains a pre-processed set (DoesNotContainChildElement). I think msxsl:node-set() is adding a root element so position is always 1. But what I need it the top 15 elements.
<xsl:variable name="Summary">
<xsl:for-each select="msxsl:node-set($DoesNotContainChildElement)">
<xsl:if test="position() < 16">
<xsl:copy-of select="."></xsl:copy-of>
</xsl:if>
</xsl:for-each>
No, the function msxsl:node-set does not add any root node, it is simply that with XSLT 1.0 a sample like
<xsl:variable name="rtf1">
<item>1</item>
<item>2</item>
<item>3</item>
</xsl:variable>
creates a result tree fragment and "A result tree fragment is treated equivalently to a node-set that contains just a single root node". So in the sample above we have a result tree fragment with a single root node containing three item child elements.
Applying the msxsl:node-set(rtf1) extension function then gives you a node-set instead of a result tree fragment where now the node-set contains a single root node with three item child elements. Thus if you want to access the item elements you need msxsl:node-set($rtf1)/* or more general msxsl:node-set($rtf1)/node() to access all child nodes.
You may try msxsl:node-set($DoesNotContainChildElement)/*. If it's true that msxsl:node-set() adds a root node, then adding /* to your path will iterate over the children, where you can test for position.
Alternatively you could simply use <xsl:apply-templates select="msxsl:node-set($DoesNotContainChildElement)/*" mode="testposition"/> and <xsl:template match="*" mode="testposition">….
There's only one variable $DoesNotContainChildElement so a for-each will only yield value 1 for position().
You can check that by running the following:
<xsl:for-each select="$x">
<pos1><xsl:value-of select="position()"/></pos1>
</xsl:for-each>
<xsl:for-each select="$x/*">
<pos2><xsl:value-of select="position()"/></pos2>
</xsl:for-each>
Where x is a (node-set type) variable.
The result will look something like
<pos1>1</pos1>
<pos2>1</pos2>
<pos2>2</pos2>
<pos2>3</pos2>
<pos2>...</pos2>
Adding <xsl:copy-of select="."/> will result in the output of the entire variable contents in case of the first for-each above, whereas for the second for-each it will result in the output of each sub-element of the variable one-by-one.
The second form is the one to use if you wish to output only selected sub-elements.
The same holds when you first apply the node-set function to change an rtf into a node-set.
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>
Within an XSL sheet, I have a nodeset in a variable, $context.
How do I properly query an attribute of the topmost node in $context? Obviously, if $context was the current node, I would just write "#attname", but it is not. Currently I do $context/../#attname which is doesn't look great to me.
EDIT
Ok, the data.
Here is what I see in the VS.
In case you wonder how I got this strange thing: I UNION'ed a node with it's subnodes and an attribute selected out from a different node:
<xsl:with-param name="context" select=". | ../#res" />.
I'm not completely aware what results from this, but I can see that it works. Highlighted is the attribute I'm interested in.
Maybe that creates an attribute attached to nothing, if that makes sence at all :|
$context/../#attname
does not make too much sense. You can't go "up" here, as this would bring you "outside of" $context.
If the node-set contains something like this (a single node)
<node attname="foo">
<bar />
</node>
then:
$context/#attname
If it is like this (a list of nodes):
<node attname="foo">
<bar />
</node>
<node attname="foo">
<bar />
</node>
then:
$context[1]/#attname
All of this does not work if the variable contains a result tree fragment (RTF). In this case, you need to employ an extension function called node-set(). Most XSLT processors provide this function.
EDIT: Your variable holds a union of the current node and a naked attribute node from its parent:
<xsl:with-param name="context" select=". | ../#res" />
The result of a union will always be in document order, so even though you selected the attribute after the context node in the XPath, in the resulting node set it will come before - the parent of the context node is before the context node in document order.
You need $context[1] to grab the attribute node, and $context[2] to grab the other node.
I must say that this is some strange and probably unnecessary complicated use of variables. I'm sure there is a way to do this in a less painful fashion. For example you could do
<xsl:with-param name="context" select="." />
and then use $context/../#res in the called template. That would be a lot more straight-forward than what you are trying now.
Also, if the <xsl:with-param> you show here is part of an <xsl:call-template>, you can drop that param entirely. When a template is called (instead of applied), then the context node does not change, passing it in is redundant.
In addition to tomalak's answer if you ever do need to propagate back up to the root you could try:
ancestor::*[not(..)]
Would love to hear of a situation where you might want/need this though.
Try it yourself by pasting:
//pet/ancestor::*[not(..)]
in this online Xpath test tool.
As you have <xsl:with-param name="context" select=". | ../#res" /> the 'res' attribute is part of the node-set (XPath 1.0) or sequence (XPath 2.0) the variable named 'context' is bound to. With XPath 2.0 you could use $context/self::attribute(res) but XPath 1.0 has no comparable expression so what you have already ($context[1] or $context/../#res) is all you can do in my view.