XSLT - Problem with foreach and incremented attribute name - xslt

I'm using XSLT and would like to transform this:
<attr>
<header name="UpdateInformation1">
<detail name="info">blah</detail>
</header>
<header name="UpdateInformation2">
<detail name="info">blah2</detail>
</header>
...other headers with different names...
</attr>
To this:
<UpdateInformation>
<info>blah</info>
</UpdateInformation>
<UpdateInformation>
<info>blah2</info>
</UpdateInformation>
...
I've been trying to do this using a foreach, but I'm not having much success. Heres what I currently have, but wildcards don't work in this type of context:
* WRONG *
<xsl:for-each select="attr/header[#name='UpdateInformation*']">
<UpdateInformation>
<Info>
<xsl:value-of select="detail[#name='info']"/>
</info>
</UpdateInformation>
</xsl:for-each>
* WRONG *
Any suggestions? Thanks!

Use something like this:
<xsl:for-each select="attr/header[starts-with(#name, 'UpdateInformation')]">
<UpdateInformation>
<Info>
<xsl:value-of select="detail[#name='info']"/>
</info>
</UpdateInformation>
</xsl:for-each>
EDITED: Corrected XPath expression per comments (below).

Doing this with the xsl:for-each element:
<xsl:for-each select="header[starts-with(#name, 'UpdateInformation')]">
<UpdateInformation>
<Info>
<xsl:value-of select="detail"/>
</info>
</UpdateInformation>
</xsl:for-each>
Using a xsl:template would be a better way to do this in xslt, as this is the strength of it:
<xsl:template match="header[starts-with(#name, 'UpdateInformation')]">
<UpdateInformation>
<Info>
<xsl:value-of select="detail"/>
</info>
</UpdateInformation>
</xsl:template>

Related

How to get XSLT key-function working with my scenario?

Here is my in-data:
<Results>
<Result>
<Id>1</Id>
</Result>
<Result>
<Id>2</Id>
</Result>
</Results>
<Results>
<RefId>1</RefId>
<Text>One</Text>
</Results>
<Results>
<RefId>2</RefId>
<Text>Two</Text>
</Results>
How the output should be:
<OBR></OBR>
<OBX>One</OBX>
<OBR></OBR>
<OBX>Two</OBX>
My xslt-code
<xsl:key name="test" match="Results/Result" use="Id"/>
<xsl:template match="Results/Result">
<OBR></OBR>
<xsl:for-each select="Results[key('test', RefId)/RefId]">
<OBX><xsl:value-of select="Text" /></OBX>
</xsl:for-each>
</xsl:template>
It does not work. My result is:
<OBR></OBR>
<OBX>One</OBX>
<OBX>Two</OBX>
<OBR></OBR>
<OBX>One</OBX>
<OBX>Two</OBX>
I assume that the problem is with the for-each in my template.. It´s looping twice every time the template runs. Any suggestions?
I complicated it unnecessarily much with they key-function. I solved with just creating a variable named ID and it´s based on the Id-field. Then in the for-each I just tested if the variable and the RefId-element matched and it works perfectly.
<xsl:template match="Results/Result">
<xsl:variable name="ID" select="Id"></xsl:variable>
<OBR></OBR>
<xsl:for-each select="Results[(RefId = $ID)]">
<OBX><xsl:value-of select="Text" /></OBX>
</xsl:for-each>
</xsl:template>

How get String before hyphen in XSLT

I want yo get the first name initial and last name.
Input :
<root>
<ele str="Great Success-Turbo Pat(by Sundon) [Cheeky Pat 8m(Monarchy)"/>
<ele str="Brylin Boyz-Scallywag"/>
<ele str="Majestic Son-Be Bee"/>
</root>
Output
<names>Great Success</name>
<names>Brylin Boyz</name>
<names>Majestic Son</name>
Tried Code:
<xsl:template match="root/name">
<names>
<xsl:value-of select="#str" />
</name>
</xsl:template>
I am using XSLT 2.0. Thank you
You need to use fn:substring-before() :
<xsl:template match="ele">
<name>
<xsl:value-of select="substring-before(#str, '-')" />
</name>
</xsl:template>

XSLT not generating expected output

Input XML:
<derivatives>
<derivative id="4" name="Audio Content">
<operator id="1" name="Reliance">
<referenceCode code="62033815">
<mobileCircle id="1" name="Maharashtra"/>
</referenceCode>
</operator>
<operator id="22" name="Aircel">
<referenceCode code="811327">
<mobileCircle id="1" name="Maharashtra"/>
</referenceCode>
</operator>
</derivative>
</derivatives>
Expected Output XML:
<hellotune>
<operator>Aircel</operator>
<vcode>811327</vcode>
</hellotune>
Current output (which is wrong):
<hellotune>
<operator>Aircel</operator>
<vcode/>
</hellotune>
XSL (which is not working):
<xsl:if test="derivatives/derivative/operator[#name='Aircel']">
<hellotune>
<operator>Aircel</operator>
<vcode><xsl:value-of select="referenceCode/#code"/></vcode>
</hellotune>
</xsl:if>
Note: Using XSL v1.0. Not mentioned the complete XSL for brevity.
Based on the XSL you provided, it can be presumed that the context node is the root node, but starting from the root node, the path referenceCode/#code does not match anything in your input. Appending derivatives/derivative/operator/ before that path would succeed in finding a referenceCode #code attribute, but it would find the wrong one. Try this push-style approach:
<xsl:template match="/">
<xsl:apply-templates select="derivatives/derivative/operator[#name='Aircel']" />
</xsl:template>
<xsl:template match="operator">
<hellotune>
<operator><xsl:value-of select="#name" /></operator>
<vcode><xsl:value-of select="referenceCode/#code"/></vcode>
</hellotune>
</xsl:template>
The xpath xpression in the element <vcode> is not referring to any node in the input document. The best way to match all the nodes is to use
//
like in your code you can use
//referenceCode/#code
(but this is only for information purpose and using same can't get your result).
you can try this way:
<xsl:template match="/">
<hellotune>
<xsl:for-each select="//operator">
<xsl:if test="./#name='Aircel'">
<operator><xsl:value-of select="#name"/></operator>
<vcode><xsl:value-of select="referenceCode/#code"/></vcode>
</xsl:if>
</xsl:for-each>
</hellotune>
</xsl:template>
Hope this helps :)

insert PCDATA from the child element into an attribute

I want to insert PCDATA from the child element into a selected node attribute
XML
<root>
<tag>
<tag1>SOME TEXT</tag1>
</tag>
</root>
MY XSL
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xi="http://www.w3.org/2001/XInclude" version="1.0">
<xsl:template match="root">
<tag-out>
<xsl:attribute name="text">
<!-- What should I select? -->
<xsl:value-of select="tag/tag1/???"/>
</xsl:attribute>
<tag-out>
</xsl:template>
...........
</xsl:stylesheet>
Desired output XML
<root-out text="SOME TEXT">
<tag-out/>
</root-out>
Thanks
What's wrong with simply doing
<tag-out text="{tag/tag1}"></tag-out>
? Of course your sample with
<tag-out>
<xsl:attribute name="text">
<xsl:value-of select="tag/tag1"/>
</xsl:attribute>
<tag-out>
is also possible. But as your post is tagged XSLT 2.0 I would at least do
<tag-out>
<xsl:attribute name="text" select="tag/tag1"/>
<tag-out>
if you really need xsl:attribute.

Finding unique nodes with xslt

I have an xml document that contains some "Item" elements with ids. I want to make a list of the unique Item ids. The Item elements are not in a list though - they can be at any depth within the xml document - for example:
<Node>
<Node>
<Item id="1"/>
<Item id="2"/>
</Node>
<Node>
<Item id="1"/>
<Node>
<Item id="3"/>
</Node>
</Node>
<Item id="2"/>
</Node>
I would like the output 1,2,3 (or a similar representation). If this can be done with a single xpath then even better!
I have seen examples of this for lists of sibling elements, but not for a general xml tree structure. I'm also restricted to using xslt 1.0 methods. Thanks!
Selecting all unique items with a single XPath expression (without indexing, beware of performance issues):
//Item[not(#id = preceding::Item/#id)]
Try this (using Muenchian grouping):
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:key name="item-id" match="Item" use="#id" />
<xsl:template match="/Node">
<xsl:for-each select="//Item[count(. | key('item-id', #id)[1]) = 1]">
<xsl:value-of select="#id" />,
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
Not sure if this is what you mean, but just in case.
In the html
<xsl:apply-templates select="item"/>
The template.
<xsl:template match="id">
<p>
<xsl:value-of select="#id"/> -
<xsl:value-of select="."/>
</p>
</xsl:template>