xsl take path and substring the value - xslt

I have this xsl path that gives me a desired value:
/path/to/#value
Is there a way to combine this into a substring?
substring(/path/to/#value, 1, 5)
The preceding statement does not work because I'm not as familiar to xsl as I thought

Actually, it should work just fine:
XML:
<?xml version='1.0'?>
<path>
<to value='123456'/>
</path>
XSLT:
<?xml version="1.0"?>
<xsl:stylesheet
version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
>
<xsl:template match="/">
<out>
<xsl:value-of select='substring(/path/to/#value, 1, 5)'/>
</out>
</xsl:template>
</xsl:stylesheet>
Another way is to use an intermediate variable:
<xsl:variable name='t' select='/path/to/#value'/>
<xsl:value-of select='substring( $t, 1, 5 )'/>

Related

Xpath not working using XSLT1.0

Based on this thread in which I am fixing the response from the sharepoint.
Previous Thread
Response
<Envelope xmlns="http://schemas.xmlsoap.org/soap/envelope/">
<Body>
<SharepointResponse xmlns="http://test.com.services.generic">
<Sharepoint_Response>
<CopyIntoItemsResponse xmlns="http://schemas.microsoft.com/sharepoint/soap/">
<CopyIntoItemsResult>0</CopyIntoItemsResult>
<Results>
<CopyResult ErrorCode="Success" DestinationUrl="http://archivelink.dev.test.com/enterprise"/>
</Results>
</CopyIntoItemsResponse>
</Sharepoint_Response>
</SharepointResponse>
</Body>
</Envelope>
I am converting this into JSONX and I need the value of <CopyIntoItemsResult>0</CopyIntoItemsResult>
< ErrorCode="Success"
and
DestinationUrl="http://archivelink.dev.test.com/enterprise"/>
Below are the Xpath I used
1) CopyIntoItemsResult <xsl:value-of select="/*[local-name()='Envelope']/*[local-name()='Body']/*[local-name()='SharepointResponse']/*[local-name()='Sharepoint_Response']/*[local-name()='CopyIntoItemsResponse']/*[local-name()='CopyIntoItemsResult']/text()"/>
2) ErrorCode /*[local-name()='Envelope']/*[local-name()='Body']/*[local-name()='SharepointResponse']/*[local-name()='Sharepoint_Response']/*[local-name()='CopyIntoItemsResponse']/*[local-name()='Results']/*[local-name()='CopyResult']/#*[local-name()='ErrorCode']
3) DestinationUrl /*[local-name()='Envelope']/*[local-name()='Body']/*[local-name()='SharepointResponse']/*[local-name()='Sharepoint_Response']/*[local-name()='CopyIntoItemsResponse']/*[local-name()='Results']/*[local-name()='CopyResult']/#*[local-name()='DestinationUrl']
The problem is I am not getting any values for and I am not able to figure it out where I am doing it wrong?
Can anyone please help.
You don't really show your XSLT but it depends on your template match. If you used the asterisk (*), your XPaths should work. Alternatively, you can use the backslash (/) in template match but remove that at beginning of each XPath.
<xsl:transform xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output version="1.0" encoding="UTF-8" indent="yes" omit-xml-declaration="no" />
<xsl:strip-space elements="*" />
<xsl:template match="*">
<data>
<CopyIntoItemsResult>
<xsl:value-of select="/*[local-name()='Envelope']/*[local-name()='Body']/*
[local-name()='SharepointResponse']/*[local-name()='Sharepoint_Response']/*
[local-name()='CopyIntoItemsResponse']/*
[local-name()='CopyIntoItemsResult']/text()"/>
</CopyIntoItemsResult>
<ErrorCode>
<xsl:value-of select="/*[local-name()='Envelope']/*[local-name()='Body']/*
[local-name()='SharepointResponse']/*[local-name()='Sharepoint_Response']/*
[local-name()='CopyIntoItemsResponse']/*[local-name()='Results']/*
[local-name()='CopyResult']/#*[local-name()='ErrorCode']"/>
</ErrorCode>
<DestinationUrl>
<xsl:value-of select="/*[local-name()='Envelope']/*[local-name()='Body']/*
[local-name()='SharepointResponse']/*[local-name()='Sharepoint_Response']/*
[local-name()='CopyIntoItemsResponse']/*[local-name()='Results']/*
[local-name()='CopyResult']/#*[local-name()='DestinationUrl']"/>
</DestinationUrl>
</data>
</xsl:template>
</xsl:transform>
OUTPUT
<data>
<CopyIntoItemsResult>0</CopyIntoItemsResult>
<ErrorCode>Success</ErrorCode>
<DestinationUrl>http://archivelink.dev.test.com/enterprise</DestinationUrl>
</data>

dyn:evaluate() inside <xsl:for-each> yields strange results

Consider the following XSL transformation:
<?xml version="1.0" encoding="UTF-8" ?>
<xsl:transform version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:exsl="http://exslt.org/common"
xmlns:dyn="http://exslt.org/dynamic"
extension-element-prefixes="dyn">
<xsl:variable name="_raw">
<test>1</test>
<test>2</test>
</xsl:variable>
<xsl:variable name="list" select="exsl:node-set($_raw)"/>
<xsl:template match="/">
<result>
<xsl:for-each select="$list/test">
<loop>
<xsl:value-of select="dyn:evaluate('exsl:node-set($list)/test')"/>
</loop>
</xsl:for-each>
</result>
</xsl:template>
</xsl:transform>
Executing this on any input gives:
<?xml version="1.0" encoding="UTF-8"?>
<result xmlns:exsl="http://exslt.org/common">
<loop>1</loop>
<loop/>
</result>
What I don't understand is:
The esxl:note-set() inside dyn:evaluate() is necessary if I want to reference $list in the XPath string. Otherwise, the first <loop> is also empty. Why? $list is already a node set.
It's exactly the same code executed twice. Why does it yield no result the second time?
This works if I don't take the values from $list, but from the XML input instead. Where's the difference?
If I remove the <xsl:for-each>, dyn:evaluate() works without the exsl:node-set() in it. Why? There's no reference to the context in the expression that is evaluated, it shouldn't make a difference.
My XSLT processor is Xalan 2.7.2.
I believe what you want to do is:
<?xml version="1.0" encoding="UTF-8" ?>
<xsl:transform version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:exsl="http://exslt.org/common"
xmlns:dyn="http://exslt.org/dynamic"
extension-element-prefixes="exsl dyn">
<xsl:variable name="_raw">
<test>1</test>
<test>2</test>
</xsl:variable>
<xsl:variable name="list" select="exsl:node-set($_raw)"/>
<xsl:template match="/">
<result>
<xsl:for-each select="$list/test">
<loop>
<xsl:value-of select="dyn:evaluate(.)"/>
</loop>
</xsl:for-each>
</result>
</xsl:template>
</xsl:transform>
Which will result in:
<?xml version="1.0" encoding="UTF-8"?>
<result><loop>1</loop><loop>2</loop></result>
Edit:
I am still not sure what you are after here, but let me point out a few details:
It's exactly the same code executed twice. Why does it yield no result
the second time?
That may be a bug in Xalan. If you run your code with libxslt, the result will be:
<?xml version="1.0"?>
<result xmlns:exsl="http://exslt.org/common"><loop>1</loop><loop>1</loop></result>
The esxl:note-set() inside dyn:evaluate() is necessary if I want to
reference $list in the XPath string. Otherwise, the first is
also empty. Why? $list is already a node set.
I don't think it's necessary. Or perhaps I don't understand what it's necessary for. In any case, changing this:
<loop>
<xsl:value-of select="dyn:evaluate('exsl:node-set($list)/test')"/>
</loop>
to:
<loop>
<xsl:value-of select="dyn:evaluate('$list/test')"/>
</loop>
will result in:
<?xml version="1.0" encoding="UTF-8"?>
<result xmlns:exsl="http://exslt.org/common"><loop>1</loop><loop>1</loop></result>
and this time the result is same for both Xalan and libxslt.

XSL condition to check if node exists

I want to check if in my XML exists node that has type attribute containing string type_attachment_.
Is it a correct way to check it?
<xsl:if test="count(*[contains(#Type, 'type_attachment_')]) > 0">
something
</xsl:if>
I don't know how nested can this node be. It can be for example as simple as that:
<?xml version="1.0"?>
<?xml-stylesheet type="text/xsl"?>
<hello-world>
<greeter>
<dsdsds>An XSLT Programmer
<greeting type = 'type_attachment_'>Hello, World!
</greeting>
</dsdsds>
</greeter>
</hello-world>
but can also contain this node nested in different other elements.
Expressions that match existing nodes are truthy. Expressions that do not match any nodes are falsy.
Therefore, you don't need to count the set of nodes returned. Simply test to see if anything matches.
<xsl:if test="*[contains(#Type, 'type_attachment')]">
something
</xsl:if>
Find out an example:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output indent="yes"/>
<xsl:param name="filt">
<filters>
<ritem type="type_attachment_" relateditemnumber="8901037"/>
<ritem relateditemnumber="8901038"/>
<ritem type="type_attachment_" relateditemnumber="8901039"/>
<ritem relateditemnumber="8901040"/>
</filters>
</xsl:param>
<xsl:template match="/">
<xsl:for-each select="$filt/filters/ritem[#type='type_attachment_']">
<xsl:copy-of select="."/>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
OUTPUT:
<ritem type="type_attachment_" relateditemnumber="8901037"/>
<ritem type="type_attachment_" relateditemnumber="8901039"/>

xsl - how to retrieve the value of a xml node based on another value of a xml node

assuming that I have following xml:
<ns5:OrderTotal>
<ns5:Name>AmountPaid</ns5:Name>
<ns5:Description>Payment Received</ns5:Description>
<ns5:SortOrder>4</ns5:SortOrder>
<ns5:Value>16.95</ns5:Value>
</ns5:OrderTotal>
<ns5:OrderTotal>
<ns5:Name>AmountDue</ns5:Name>
<ns5:Description>Current Amount Due</ns5:Description>
<ns5:SortOrder>5</ns5:SortOrder>
<ns5:Value>0.0</ns5:Value>
</ns5:OrderTotal>
If I only want print the Value of the OrderTotal when it Name is AmountPaid, how could achieve this?
Thank you for the helps.
I don't know how you are going to print the value you need, but it can be gathered using the following XPath expression:
//ns5:OrderTotal[child::ns5:Name = 'AmountPaid']/ns5:Value
For example:
XML:
<ns5:Orders xmlns:ns5="a">
<ns5:OrderTotal>
<ns5:Name>AmountPaid</ns5:Name>
<ns5:Description>Payment Received</ns5:Description>
<ns5:SortOrder>4</ns5:SortOrder>
<ns5:Value>16.95</ns5:Value>
</ns5:OrderTotal>
<ns5:OrderTotal>
<ns5:Name>AmountDue</ns5:Name>
<ns5:Description>Current Amount Due</ns5:Description>
<ns5:SortOrder>5</ns5:SortOrder>
<ns5:Value>0.0</ns5:Value>
</ns5:OrderTotal>
</ns5:Orders>
XSL:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xd="http://www.oxygenxml.com/ns/doc/xsl" version="1.0"
xmlns:ns5="a">
<xsl:template match="/">
<xsl:element name="Value">
<xsl:value-of select="//ns5:OrderTotal[child::ns5:Name = 'AmountPaid']/ns5:Value"/>
</xsl:element>
</xsl:template>
</xsl:stylesheet>
Output:
<?xml version="1.0" encoding="utf-8"?>
<Value>16.95</Value>
<xsl:if test="Name = 'AmountPaid'">
<xsl:value-of select="Value"/>
</xsl:if>
Try the above XSL.

XSLT node Traversal

Here is a snip-it of the XML:
<?xml version="1.0" encoding="iso-8859-1" ?>
<NetworkAppliance id="S123456">
<Group id="9">
<Probe id="1">
<Value>74.7</Value>
</Probe>
</NetworkAppliance>
I want to get the single point value of 74.7. There are many groups with unique ID's and many Probes under that group with unique ID's each with values.
I am looking for example XSLT code that can get me this one value. Here is what i have that does not work:
<?xml version="1.0" ?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output method="html" version="3.2" />
<xsl:template match="NetworkAppliance">
<xsl:apply-templates select="Group[#id='9']"/>
</xsl:template>
<xsl:template match="Group">
Temp: <xsl:value-of select="Probe[#id='1']/Value"/>
<br/>
</xsl:template>
</xsl:stylesheet>
Here is what worked for me in the end:
<?xml version="1.0" encoding="ISO-8859-1"?>
<!-- Edited by XMLSpy® -->
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
<html>
<body>
<xsl:for-each select="NetworkAppliance/Group[#id=9]/Probe[#id=1]">
Value: <xsl:value-of select="Value" />
</xsl:for-each>
</body>
</html>
</xsl:template>
</xsl:stylesheet>
Don't forget that you can do select several levels at once. Fixing your XML to:
<?xml version="1.0" encoding="iso-8859-1" ?>
<NetworkAppliance id="S123456">
<Group id="9">
<Probe id="1">
<Value>74.7</Value>
</Probe>
</Group>
</NetworkAppliance>
and using this stylesheet:
<?xml version="1.0"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output method="html" version="3.2" />
<xsl:template match="/">
Temp: <xsl:value-of select="//Group[#id='9']/Probe[#id='1']/Value"/>
<br/>
</xsl:template>
</xsl:stylesheet>
we can pick out that one item you're interested in.
Points to note:
The // part of the expression means that the search for Group nodes takes place throughout the whole tree, finding Group nodes at whatever depth they're at.
The [#id='9'] part selects those Group nodes with id of 9
The Probe[#id='1'] part immediately after that selects those children of the Group nodes it found where the id is 1, and so on.
<xsl:value-of select="/NetworkAppliance/Group[#id=9]/Probe[#id=1]/Value"/>
XSLT is just one of the tools in the box, and nothing without XPath.
the xpath for value of a node is /node/text()
So
<xsl:value-of select="Probe[#id='1']/text()"/>