XSL : How to pass variable to for each - xslt-1.0

<xsl:for-each select="$script/startWith">
<xsl:variable name = "i" >
<xsl:value-of select="$script/startWith[position()]"/>
</xsl:variable>
<xsl:for-each select="JeuxDeMots/Element">
<xsl:variable name = "A" >
<xsl:value-of select="eName"/>
</xsl:variable>
<xsl:if test="starts-with($A,$i)= true()">
<xsl:variable name="stringReplace">
<xsl:value-of select="substring($i,0,3)"/>
</xsl:variable>
<xsl:value-of select="$stringReplace"/>
</xsl:if>
</xsl:for-each>
</xsl:for-each>
Problem : variable $i, can not pass in an xsl for-each.
Please help me.

Try replacing the declaration of i with
<xsl:variable name="i" select="string(.)"/>
The context item (i.e. ".") is different for each evaluation of the for-each instruction, but the value of the expression $script/startWith[position()] does not change. (Here, you are making a sequence of startWith elements, and testing each one for the effective boolean value of the expression position(). The expression position() returns a positive integer, so its effective boolean value is always true. So the predicate [position()] is doing no work at all here.
Also, you'll want to replace the declaration of stringReplace with
<xsl:variable name="stringReplace" select="substring($i,1,3)"/>
(String offsets begin with 1 in XPath, not with 0.)
I am guessing that you want to process all of the startWith children of $script, and for each one emit the first three characters of the startWith value once, for each startWith/JeuxDeMots/Element whose eName child begins with the value of startWith.
Actually, the whole thing might be a little easier to read if it were briefer and more direct:
<xsl:for-each select="$script/startWith">
<xsl:variable name = "i" select="string()"/>
<xsl:for-each select="JeuxDeMots/Element">
<xsl:if test="starts-with(eName,$i)">
<xsl:value-of select="substring($i,1,3)"/>
</xsl:if>
</xsl:for-each>
</xsl:for-each>
It makes perfect sense to create the variables $A and $stringReplace if they are used several times in code you're not showing us, but if not, ...

I think the problem is that you change context in first for-each. Then the element JeuxDeMots is not "visible" for second for-each. You can for example try to store it in variable and use this variable in second for-each (there are also another ways how to figure out this problem).
<xsl:template match="/">
<xsl:variable name="doc" select="JeuxDeMots"/>
<xsl:choose>
<xsl:when test="$script/startWith">
<xsl:for-each select="$script/startWith">
<xsl:variable name="i">
<xsl:value-of select="."/>
</xsl:variable>
<xsl:for-each select="$doc/Element">
<xsl:variable name="A">
<xsl:value-of select="eName"/>
</xsl:variable>
<xsl:if test="starts-with($A,$i) = true()">
<xsl:variable name="stringReplace">
<xsl:value-of select="substring($i,0,3)"/>
</xsl:variable>
<xsl:value-of select="$stringReplace"/>
</xsl:if>
</xsl:for-each>
</xsl:for-each>
</xsl:when>
</xsl:choose>
</xsl:template>
Although I'm not sure what exactly you are dealing with it seems to output desired value AsAt.
You could also consider C.M.Sperberg-McQueen post about string offset in XPath.

Related

Using XSLT1 how can I simply assign an incrementing value beginning with 1?

In my XSL script I have the following:
<body>
<xsl:for-each select="MeetingWorkBook/Meeting">
<xsl:apply-templates select="TFGW/BibleReadingItem/Readers/Reader"/>
<xsl:apply-templates select="AYFM/StudentItem/Students/Student"/>
</xsl:for-each>
</body>
The start of the template is:
<xsl:template match="Student | Reader">
<xsl:if test="self::Student">
<hr/>
</xsl:if>
<div>
<xsl:attribute name="id">
<xsl:text>containter-student-slip</xsl:text>
<xsl:variable name="pos" select="position()"/>
<xsl:value-of select="$pos"/>
</xsl:attribute>
I want the id to simply be a numerical value, starting at 1 and up.
I realise it is no good using position() because it is relative to both matching element types.
Using XSLT1 how can I simply assign an incrementing value beginning with 1?
If you want to use position(), you could probably* just change:
<xsl:for-each select="MeetingWorkBook/Meeting">
<xsl:apply-templates select="TFGW/BibleReadingItem/Readers/Reader"/>
<xsl:apply-templates select="AYFM/StudentItem/Students/Student"/>
</xsl:for-each>
to:
<xsl:for-each select="MeetingWorkBook/Meeting">
<xsl:apply-templates select="TFGW/BibleReadingItem/Readers/Reader | AYFM/StudentItem/Students/Student"/>
</xsl:for-each>
Alternatively*, it may be possible to use xsl:number.
(*) Of course, without a reproducible example these are merely guesses.

How do I use a variable in select in xsl

now
<xsl:for-each select="tm-reg-gaz/trademark/img">
want to change like this.
<xsl:variable name="tag_name">
<xsl:if test="tm-reg-gaz">tm-reg-gaz</xsl:if>
<xsl:if test="mp-tm-reg-gaz">mp-tm-reg-gaz</xsl:if>
</xsl:variable>
<xsl:for-each select="$tag_name/trademark/img">
</xsl:for-each>
I tried these
<xsl:for-each select="$tag_name/trademark/img">
<xsl:for-each select="path[$tag_name]/trademark/img">
<xsl:for-each select="{$tag_name}/trademark/img">
Let me know if it can. Thank you.
You can use select="*[name()=$tagname]/trademark/img".
A better approach is probably
<xsl:variable name="selection" select="tm-reg-gaz|mp-tm-reg-gaz">
<xsl:for-each select="$selection/trademark/img">
</xsl:for-each>
(But the details depend on whether both elements can appear or whether they are mutually exclusive).
Here is my proposal, feel free to shoot it down:
<xsl:variable name="variable" as="node()*">
<xsl:choose>
<xsl:when test="$testme = 'something'">
<xsl:copy-of select="//tm-reg-gaz/trademark/img"/>
</xsl:when>
<xsl:otherwise>
<xsl:copy-of select="//mp-tm-reg-gaz/trademark/img"/>
</xsl:otherwise>
</xsl:choose>
</xsl:variable>
<xsl:for-each select="$variable">
<!-- do something -->
</xsl:for-each>

xslt 3.0 conditional dynamic element creation

I have a variable:
<xsl:variable name="courseType" select="Record[1]/course-type"/>
and based on a value I would like to create a dynamic element:
<xsl:if test="$courseType ='B'">
<xsl:element name="newElement">
</xsl:if>
...
other nodes
...
<xsl:if test="$courseType ='B'">
</xsl:element>
</xsl:if>
The problem is that:
The element type "xsl:element" must be terminated by the matching end-tag "".
is there any way to achieve this?
You haven't provided much content but you can certainly use e.g.
<xsl:variable name="other-nodes">
...
</xsl:variable>
<xsl:choose>
<xsl:when test="$course-type = 'B'">
<newElement>
<xsl:sequence select="$other-nodes"/>
</newElement>
</xsl:when>
<xsl:otherwise>
<xsl:sequence select="$other-nodes"/>
</xsl:otherwise>
</xsl:choose>
Note that I have used a literal result element for newElement instead of using xsl:element as I only consider the use of xsl:element necessary if you want to compute the element name at run-time but it does not matter for the solution of the original problem whether you use a literal result element or xsl:element.

How to multiply numbers with each other in xslt 1.0

I have a number for example 123, this is stored in an element let's say
<Number>123</Number>. I am looking for a solution written in XSLT 1.0 which can do something like: 1*2*3 and provide me the result as 6. The value in Number element can be in any length. I know i can do this through substring function and by storing the values one by one in variables but the problem is, i dont know the length of this field.
I could not write any xslt for this.
Can anyone help or suggest a solution for this?
You can do this by calling a recursive named template:
<xsl:template name="digit-product">
<xsl:param name="digits"/>
<xsl:param name="prev-product" select="1"/>
<xsl:variable name="product" select="$prev-product * substring($digits, 1, 1)" />
<xsl:choose>
<xsl:when test="string-length($digits) > 1">
<!-- recursive call -->
<xsl:call-template name="digit-product">
<xsl:with-param name="digits" select="substring($digits, 2)"/>
<xsl:with-param name="prev-product" select="$product"/>
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$product"/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
Working example: http://xsltransform.net/3NJ38YJ
Note that this assumes the value of the passed digits parameter is an integer.

xslt replace value of multiple elements

I need to replace values of multiple elements of an input xml. The input file has around 300 elements of which I need to replace around 100 elements. I have used identity template before, but I think it would require me to write 100 different templates each for replacing a single element value. I am not very good at xslts, so, am I thinking it right, or is there a better an elegant approach? Please advice.
Edit
Here is the Link of sample input xml.
The output will have almost the same structure, but different values for some of the elements.
Well, something I did similar before before so I am happy to share ... modified to your example BUT does not do the value mapping, it does name mapping.
First create a simple mapping file like this (NOTE: you would need the namespaces to do this right
<env:map xmlns:env="http://www.w3.org/2003/05/soap-envelope">
<elem old="CardCode" new="CodeCardNEW"/>
<elem old="CardName" new="IAMNew"/>
</env:map>
Then one template will do you with a lookup. There are likely better ways to do the mapping, I am just for-each looping over them all ... a key would be better. But this gets you there.
Output from your file with above:
<env:Envelope xmlns:env="http://www.w3.org/2003/05/soap-envelope">
<env:Body>
<GetByKeyResponse>
<BOM>
<BO>
<AdmInfo>
<Object>oBusinessPartners</Object>
</AdmInfo>
<BusinessPartners>
<row>
<CodeCardNEW>CR43WEB</CodeCardNEW>
<IAMNew>Zack Burns</IAMNew>
<CardType>cCustomer</CardType>
...
And here's the XSL:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:env="http://www.w3.org/2003/05/soap-envelope"
version="1.0">
<xsl:param name="map" select="document('map.xml')/env:map"></xsl:param>
<xsl:template match="text()" priority="1">
<xsl:value-of select="."/>
</xsl:template>
<xsl:template match="#*|node()">
<xsl:variable name="newname">
<xsl:call-template name="namesub">
<xsl:with-param name="name" select="name()"/>
</xsl:call-template>
</xsl:variable>
<xsl:element name="{$newname}">
<xsl:apply-templates select="#*|node()"/>
</xsl:element>
</xsl:template>
<xsl:template name="namesub">
<xsl:param name="name"/>
<xsl:variable name="newname">
<xsl:for-each select="$map/elem">
<xsl:choose>
<xsl:when test="#old = $name">
<xsl:value-of select="#new"/>
</xsl:when>
</xsl:choose>
</xsl:for-each>
</xsl:variable>
<xsl:choose>
<xsl:when test="string-length($newname) > 0">
<xsl:value-of select="$newname"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$name"/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
You should be able to adapt this as long as you have a match on name ... if the name is the same in different hierarchies of the XML, you would need more work.