I have a global variable name="cats" as="element()*. The data I used to create the variable and the data I want to transform are in different parts of the tree of the document.
Let's say the variable contains the elements CellA, CellF and CellC (enclosed in <Item> tags).
My goal is to fill the following predefined structure with data from a table which contains as many cells <field> per <line> as the variable (and in the same order).
<row>
<CellA> </CellA>
<CellB> </CellB>
<CellC> </CellC>
<CellD> </CellD>
<CellE> </CellE>
<CellF> </CellF>
</row>
My idea is to check for each <Cell_> if the name exists in the variable and use its position to access the original data.
Unfortunately, I cannot use position() while looping through $cats because I am not able to access <field> here.
Another method, which was suggested elsewhere, would be to count the number of preceding-siblings where $cats = Cell_. However, the suggestion was not based on variables. And (due to being a novice) I was not able to figure out how to do this.
Is it possible to do it this way? Is there another way to do it?
If something is not clear, please let me know.
Additional information
sourceXML
<body>
<line>
<field>data</field>
<field/>
<field/>
</line>
<line/>
...
</body>
targetXML (see above)
XSLT
<xsl:variable name="cats" as="element()*">
<Item>CellA</Item>
<Item>CellF</Item>
<Item>CellC</Item>
</xsl:variable>
<xsl:for-each select="body/line">
<row>
<CellA>
*What to do here to fill it with data from source xml*
<xsl:variable name="pos" select="???"/>
<xsl:value-of select="field[$pos]"/>
</CellA>
<CellB>
</CellB>
...
</row>
My idea is to check for each <Cell_> if the name exists in the
variable and use its position to access the original data.
Apparently you want to use the index-of() function.
Related
I am trying to generate the following structure in an XSLT template.
<ns:e1>
<child1>some value<child1>
<child2>some value<child2>
<child3>some value<child3>
</ns:e1>
or
<ns:e2>
<child1>some value<child1>
<child2>some value<child2>
<child3>some value<child3>
</ns:e2>
or other elements ns:e3 etc (although finite), based on a template parameter (say type). Typically I could use an xls:choose construct. In such a case, I would be duplicating the child elements (whose values are also template parameters).
Is there a way in XSLT to dynamically assume the element name ns:e1 or ns:e2 so that I can put the child elements once in its parent. I could save maintenance effort later if I have change the child elements or values (change once in one place and avoid bugs due to human errors).
Thanks for your help in advance.
Yes, you may use the xsl:element instruction to do that.
Assuming you always want to have <child1>some value<child1><child2>some value<child2><child3>some value<child3> as children for your parent element, you could rewrite your code like so:
<xsl:variable name="elementName">
<!-- compute the element name here ... -->
</xsl:variable>
<!-- Here we create an element having the name computed in variable elementName -->
<xsl:element name="{$elementName}" namespace="http://www.anamespace.com/and/so/on">
<child1>some value<child1>
<child2>some value<child2>
<child3>some value<child3>
</xsl:element>
I have data in the below XML format
<item>
<title>Body Cleaner</title>
<vendor>Wipro</vendor>
<location>EMEA</location>
<manufacture_date>12/08/2010</manufacture_date>
<item_type>House Hold</item_type>
<item_type>Health</item_type>
</item>
<item>
<title>Sweet Catch up</title>
<vendor>Unilever</vendor>
<location>APAC</location>
<manufacture_date>21/07/2013</manufacture_date>
<item_type>House Hold</item_type>
<item_type>Kitchen</item_type>
</item>
(1) Below is code in a xsl file
<xsl:key name="groups" match="item_type" use="."/>
and
<xsl:apply-templates select="item/item_type[generate-id() = generate-id(key('groups', .)[1])]"/>
here i am unable to understand the use of generate-id() in that particular case . What is the purpose of below code
[generate-id() = generate-id(key('groups', .)[1])]
(1) Below is code in another xsl file
<xsl:key name="vendors" match="item" use="vendor"/>
and
<xsl:apply-templates select="item[count(.|key('vendors',vendor)[1])=1]">
here i am unable to understand the expression
item[count(.|key('vendors',vendor)[1])=1]
specially the purpose of .| in count.
Could somebody help me to make the ground here so that i can further understand the XSLT code.
Thanks
This is called muenchian grouping. In xslt 1.0, there was no built in grouping.
Keys, like this one are used by the key() function:
<xsl:key name="vendors" match="item" use="vendor"/>
The key() function returns a node-set from the document, using the index specified by an element (info).
item[count(.|key('vendors',vendor)[1])=1]
This will see whether a node set is made up of the two nodes has one or two nodes in it (e.g.grouping). This basically identifies the groups. Once you have Identified the groups you can visit each node in the key.
I have one xml, and I have to find first n element whose child element has one of allowed value
e.g. for following xml, I want to select element whose state is WA or NY. List of allowed state is dynamic value so I can't use
<xsl:apply-templates select="element[(state='WA' or state='NY')]"/>
when I am trying to filter it with contains, nothing is happening. e.g.
<xsl:variable name="allowedListPadded">;WA;NY;</xsl:variable>
<xslt:apply-templates select="element[contains($allowedListPadded,concat(';',state,';'))]"/>
XML:
<items>
<element>
<state>WA</state>
<title>Washington</title>
</element>
<element>
<state>OR</state>
<title>Oragon</title>
</element>
<element>
<state>NY</state>
<title>New York</title>
</element>
<element>
<state>WA</state>
<title>Washington News</title>
</element>
<element>
<state>TX</state>
<title>Texas</title>
</element>
</items>
I was thinking to filter elements in apply-templates and then in template, wants to use position() < n. However got stuck with first part only.
complete xslt as asked.
<xslt:stylesheet version="1.0" xmlns:xslt="http://www.w3.org/1999/XSL/Transform"
exclude-result-prefixes="xslt">
<xslt:output omit-xml-declaration="yes" method="xml" />
<xslt:template match="root">
<xslt:text>{"statelist":</xslt:text>
<xslt:choose>
<xslt:when test="$allowedListPadded=''">
<!-- if no list is present, give default state -->
<xslt:apply-templates select="element[state = 'WA']"/>
</xslt:when>
<xslt:otherwise>
<xslt:apply-templates select="element[contains(allowedListPadded,concat(';',state,';'))]"/>
</xslt:otherwise>
</xslt:choose>
<xslt:text>}</xslt:text>
</xslt:template>
</xslt:stylesheet>
<xslt:apply-templates select="element[contains(allowedListPadded,concat(';',state,';'))][position() < 5]"/>
The trick here is not using an and operator in your predicate, but to use two predicates. The first one creates a node-set including the selected elements, and the second one returns a node-set with 5 (for example) elements of this node-set.
You are matching root element and there isn't any root element in your xml. You must change that and match items element. Your approach:
<xslt:variable name="allowedListPadded">;WA;NY;</xslt:variable>
<xslt:apply-templates select="element[contains($allowedListPadded,concat(';',state,';'))]"/>
should work then.
I have an image (in the content) with a src attribute
http://myPage/rss.gif
I wan't to change the attribute. The xsl:template match expression doesn't work..
<replace css:content="#content" css:theme=".content" />
<xsl:template match="img/#src[contains(., 'rss.gif')]">
<xsl:attribute name="src">/++theme++myPackage/images/<xsl:value-of select="." /></xsl:attribute>
</xsl:template>
What am I doing wrong?
In your example you select an img-tag containing the string rss.gif in its src-attribute, this string is probably http://myPage/rss.gif.
Then you add an attribute src to the img-tag beeing its value the concatenation of the strings /++theme++myPackage/images/ and the original value http://myPage/rss.gif.
The original value is added by <xsl:value-of select="." /></xsl:attribute>. This selects the whole value of the src-attribute!
Thus you get something like:
<img id="content" src="/++theme++myPackage/images/http://myPage/rss.gif" />
For test purposes you could first add an img-element whith the expected src-attribute to your theme.html file and check if the image file is accessed. Please check also that the value of src in your content file only contains the string that you want to append to /++theme++myPackage/images/.
BTW, in your example you replace the theme element by class: css:theme=".content". Remind that this will replace all elements with the class content. Consider using id or a narrower selector e.g. div.content or better div#someid.content.
I'm looking to use a variable as part of an XPath expression.
My problem might be the msxsl node-set function... not sure. But don't let that cloud your judgement... read on...
I'm using a .NET function to load up the content file, which is passed in via bespoke XML content. The #file results in an XML file.
The bespoke XML the sits on the page looks like :
<control name="import" file="information.xml" node="r:container/r:group[#id='set01']/r:item[1]" />
The XSL looks like :
<xsl:variable name="document">
<xsl:copy-of select="ext:getIncludedContent(#file)" />
</xsl:variable>
I'm then translating this to a node-set so I can query the document
<xsl:variable name="preset-xml" select="msxsl:node-set($document)" />
The source file I am loading in looks like :
<container>
<group id="set01">
<item>value01</item>
<item>value02</item>
<item>value03</item>
</group>
<group id="set02">
<item>value04</item>
<item>value05</item>
</group>
</container>
It works up until this point. I can see the source file being brought thru and output as XML.
My problem comes in when I am trying to query the source file with an XPath expression fed in from the content.
I've tried :
<xsl:value-of select="$preset-xml/#node" />
Clearly that doesn't work as it looks for #node as a direct child of the loaded in XML.
I've tried :
<xsl:variable name="node" select="#node" />
<xsl:value-of select="$preset-xml/$node" />
But it doesn't like the second variable.
I've tried concat($preset-xml,'/',$node), but that still draws out the entire document in the result.
The only way I can get this working at the moment is to write out the full expression in the template :
<xsl:value-of select="$preset-xml/r:container/r:group[#id='set01']/r:item[1]" />
Which correctly brings thru value01
But that is then a hard coded solution.
I want to develop a solution which can be manipulated from the content to suit any sort of imported content, with the parameters declared in the content file.
p.s. I'm using XSLT 1.0 because the tech admin won't change the Microsoft parser to support later versions of XSL, so any solution would need to be written with that in mind.
There is no feature similar to reflection in XSLT. MS XSLT engine, in particular, compiles XPath queries when the XSLT was loaded, not when it was applied to your XML document. There is a reason to this: it separates the code (XSLT) from the data (input XML).
What are you trying to achieve by mixing up code and data; are you sure you really need to execute an arbitrary XPath expression from the input XML? Think, for example, what if the user will pass in the #name equal to e.g. //*, and then you'll send to the output the content of the entire input XML document, which might contain some sensitive data.
If you really need to do the thing you described, you may write your own XSLT extension object, which will accept the document root and the XPath query and will return the latter applied to the former. Then you'll be able to call it like that:
<xsl:value-of select="myExtensionNs:apply-xpath($preset-xml, #node)"/>
As for your original attempt with concat($preset-xml,'/',$node), concat is the string concatenating function. You supply some arguments to it, and it returns the concatenation of its string representations. So, concat($preset-xml,'/',$node) will return the string value of $preset-xml, concatenated with /, concatenated with the string value of $node. If you were trying to write a needed XPath query, concat('$preset-xml','/',$node) would seem more logical (note the quotes); still, there is no way to evaluate the resulted string (e.g. $preset-xml/r:container/r:group[#id='set01']/r:item[1]) to the value of corresponging XPath query; it could only be sent to output.
One possible trick is to call an parametrized xsl:template which carries out the crucial select comparison part and evaluate it's output as string.
I had a similar problem (count all nodes of an arbitrary attribute value) which I solved this way (btw: $sd = 'document($filename)' so I load a secondary xml files content):
<xsl:template match="/">
<xsl:variable name="string_cntsuccess">
<xsl:for-each select="./Testcase">
<xsl:variable name="tcname" select="#location"/>
<xsl:for-each select="$sd//TestCase[#state='Passed']">
<xsl:call-template name="producedots">
<xsl:with-param name="name" select="$tcname"/>
</xsl:call-template>
</xsl:for-each>
</xsl:for-each>
</xsl:variable>
<xsl:variable name="cntsuccess" select="string-length($string_cntsuccess)"/>
</xsl:template>
<xsl:template name="producedots">
<xsl:param name="name"/>
<xsl:variable name="ename" select="#name"/>
<xsl:if test="$name = $ename">
<xsl:text>.</xsl:text>
</xsl:if>
</xsl:template>