Transforming XML content to array - arrays

As the reverse case of this post, how it's possible to transform content of an XML document to array using XSLT.
For this:
<Records>
<item>value1</item>
<item>value2</item>
<item>value3</item>
<item>value4</item>
<item>value5</item>
</Records>
The desired result is something like this:
[value1, value2, value3, value4, value5]
What's the idea?

Hope this helps..
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" exclude-result-prefixes="xsl">
<xsl:output method="text"/>
<xsl:template match="/">
<xsl:variable name="countItems" select="count(Records/item)"/>
[<xsl:for-each select="Records/item">
<xsl:value-of select="."/>
<xsl:choose>
<xsl:when test="position()=$countItems"/>
<xsl:otherwise>,</xsl:otherwise>
</xsl:choose>
</xsl:for-each>]
</xsl:template>
</xsl:stylesheet>
XSLT 1.0 can be used to generate a resultant xml,text or HTML file from the source xml file.
You can get the resultant data from the file created via XSLT

Related

XSLT count items in array

I'm trying to modify an XLST 1.0 file and I found that I can use an array like this:
<xsl:variable name="array">
<Item>106</Item>
<Item>107</Item>
</xsl:variable>
Now I want to write an IF structure where i have a test on the amount of items in the array.
I'v tried this, but this isn't working:
<xsl:if test="count($array) = 0"></xsl:if>
Am I using the right approach for this problem?
First, there are no "arrays" in XML.
Next, count($array) in your example will always return 1, because your variable contains a single parent node. To count the child Item nodes, you would need to use count($array/Item).
However, that too would fail, because in XSLT 1.0 your variable contains a result-tree-fragment - and XSLT 1.0 can only count nodes in a node-set.
One solution is to turn the RTF into a node-set, using an extension function (which is supported by practically all XSLT 1.0 processors). For example, the following stylesheet:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:exsl="http://exslt.org/common"
extension-element-prefixes="exsl">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:variable name="array-rtf">
<Item>106</Item>
<Item>107</Item>
</xsl:variable>
<xsl:variable name="array" select="exsl:node-set($array-rtf)" />
<xsl:template match="/">
<test>
<xsl:value-of select="count($array/Item)"/>
</test>
</xsl:template>
</xsl:stylesheet>
returns:
<?xml version="1.0" encoding="UTF-8"?>
<test>2</test>
Another option is to use an internal element instead of a variable:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:my="http://example.com/my"
exclude-result-prefixes="my">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<my:array>
<Item>106</Item>
<Item>107</Item>
</my:array>
<xsl:template match="/">
<test>
<xsl:value-of select="count(document('')/*/my:array/Item)"/>
</test>
</xsl:template>
</xsl:stylesheet>

Create an XSLT that will convert an array into unique sequential elements

I am trying to create an XSLT that will convert an array into unique sequential elements. I am probably not explaining this correctly so I will show you:
Current XML
<?xml version="1.0" encoding="UTF-8"?>
<DocumentRequests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<PlanID>20151014_103605</PlanID>
<LetterType>
<Fund>Yes</Fund>
<Adding>Yes</Adding>
<Bau>Yes</Bau>
</LetterType>
<PlanNumbers>
<PlanNumber>A01</PlanNumber>
<PlanNumber>A02</PlanNumber>
<PlanNumber>A03</PlanNumber>
<PlanNumber>A04</PlanNumber>
<PlanNumber>A05</PlanNumber>
<PlanNumber>A06</PlanNumber>
<PlanNumber>456</PlanNumber>
</PlanNumbers>
</DocumentRequests>
Current XSLT
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="3.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:fn="http://www.w3.org/2005/xpath-functions">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:template match="*">
<SourceFeedbackTransformed>
<PlanAdminID><xsl:value-of select="PlanAdminID" /></PlanAdminID>
<xsl:for-each select="LetterType">
<xsl:copy-of select="*" copy-namespaces="no"/>
</xsl:for-each>
<xsl:for-each select="PlanNumbers">
<xsl:copy-of select="*" copy-namespaces="no"/>
</xsl:for-each>
</SourceFeedbackTransformed>
</xsl:template>
</xsl:stylesheet>
Current output
<?xml version="1.0" encoding="UTF-8"?>
<SourceFeedbackTransformed xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:fn="http://www.w3.org/2005/xpath-functions">
<PlanAdminID/>
<Fund>Yes</Fund>
<Adding>Yes</Adding>
<Bau>Yes</Bau>
<PlanNumber>A01</PlanNumber>
<PlanNumber>A02</PlanNumber>
<PlanNumber>A03</PlanNumber>
<PlanNumber>A04</PlanNumber>
<PlanNumber>A05</PlanNumber>
<PlanNumber>A06</PlanNumber>
<PlanNumber>456</PlanNumber>
</SourceFeedbackTransformed>**
Desired Output
<?xml version="1.0" encoding="UTF-8"?>
<SourceFeedbackTransformed xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:fn="http://www.w3.org/2005/xpath-functions">
<PlanAdminID/>
<Fund>Yes</Fund>
<Adding>Yes</Adding>
<Bau>Yes</Bau>
<PlanNumber1>A01</PlanNumber1>
<PlanNumber2>A02</PlanNumber2>
<PlanNumber3>A03</PlanNumber3>
<PlanNumber4>A04</PlanNumber4>
<PlanNumber5>A05</PlanNumber5>
<PlanNumber6>A06</PlanNumber6>
<PlanNumber7>456</PlanNumber7>
</SourceFeedbackTransformed>
As you can see the Array with 7 values has been converted to 7 different elements.
Thank you for your help.
Cheers,
You can get the result you're after quite easily by:
XSLT 2.0
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="/DocumentRequests">
<SourceFeedbackTransformed>
<PlanAdminID><xsl:value-of select="PlanID" /></PlanAdminID>
<xsl:copy-of select="LetterType/*" copy-namespaces="no"/>
<xsl:for-each select="PlanNumbers/PlanNumber">
<xsl:element name="PlanNumber{position()}">
<xsl:value-of select="."/>
</xsl:element>
</xsl:for-each>
</SourceFeedbackTransformed>
</xsl:template>
</xsl:stylesheet>
Note:
I have changed your:
<xsl:value-of select="PlanAdminID" />
to:
<xsl:value-of select="PlanID" />
as there is no PlanAdminID element in your input. If you really want to output an empty <PlanAdminID/> element as shown in your requested output, then you could do so directly, without fetching the value of a non-existing node.

Creating dynamic array in XSLT

I have a requirement where I have to concatenate each of the <troubleticketalarm> details into a one string per alarm, then map it to Description field in my xslt.
Description is of type array in my xslt.
Here is my xml,
<ticketDetails>
<alarms>
<troubleticketalarm>
<alarmId>3117</alarmId>
<alarmName>OSS</alarmName>
<amo>amo</amo>
<alarmedEquipment>alarmedEquipment</alarmedEquipment>
<severity>1</severity>
<specificProblem>specificProblem</specificProblem>
<probableCause>probableCause</probableCause>
<activationDate>2015-03-17T17:33:04</activationDate>
<associationDate>2015-03-17T17:33:04</associationDate>
<clearDate>2015-03-17T17:33:04</clearDate>
</troubleticketalarm>
<troubleticketalarm>
<alarmId>3118</alarmId>
<alarmName>OSS123</alarmName>
<activationDate>2015-03-17T17:33:0405:30</activationDate>
<asociationDate>2015-03-17T17:33:0405:30</asociationDate>
<clearDate></clearDate>
</troubleticketalarm>
<troubleticketalarm>
<alarmId>3119</alarmId>
<alarmName>OSS124</alarmName>
<amo>amo</amo>
<alarmedEquipment>alarmedEquipment</alarmedEquipment>
<severity>1</severity>
<activationDate>2015-03-17T17:33:0405:30</activationDate>
<asociationDate>2015-03-17T17:33:0405:30</asociationDate>
<clearDate></clearDate>
</troubleticketalarm>
</alarms>
</ticketDetails>
Here is the part of xslt :
<ns:Description type="Array">
<ns:Description type="String" mandatory="" readonly=""></ns:Description>
</ns:Description>
What I want is to concatenate values for separated by '|' and map it to the ns:Description for each concatenated value of
Concatenated string looks something like this
3117|OSS|amo|alarmedEquipment|1|specificProblem|probableCause|2015-03-17T17:33:04+05:30|2015-03-17T17:33:04+05:30|2015-03-17T17:33:04+05:30
3118|OSS123|||||||2015-03-17T17:33:04+05:30|
3119|OSS124|amo|alarmedEquipment|1|||2015-03-17T17:33:04+05:30|2015-03-17T17:33:04+05:30|
These 3 strings should be added to Description[0],Description[1],Description[1].
There could be multiple <troubleticketalarm> tags in xml so the xslt Description should be dynamic.
How do I achieve this? Please let me know if question is not clear.
Edit
My XSLT file looks something like
<xsl:stylesheet version='2.0' xmlns:xsl='http://www.w3.org/1999/XSL/Transform'>
<xsl:output method="xml" encoding="UTF-8" indent="yes" />
<xsl:template match='/'>
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ns="http://schemas.hp.com/SM/7" xmlns:com="http://schemas.hp.com/SM/7/Common" xmlns:xm="http://www.w3.org/2005/05/xmlmime">
<soapenv:Header/>
<soapenv:Body>
<ns:CloseIncidentRequest attachmentInfo="" attachmentData="" ignoreEmptyElements="true" updateconstraint="-1">
<ns:model query="">
<ns:keys query="" updatecounter="1">
<ns:IncidentID type="String" mandatory="" readonly=""></ns:IncidentID>
</ns:keys>
<ns:instance query="" uniquequery="" recordid="" updatecounter="1">
<ns:IncidentID type="String" mandatory="" readonly=""></ns:IncidentID>
<ns:Description type="Array">
<ns:Description type="String" mandatory="" readonly=""></ns:Description>
</ns:Description>
......
......
</ns:instance>
</ns:model>
</ns:CloseIncidentRequest>
</soapenv:Body>
</soapenv:Envelope>
</xsl:template>
</xsl:stylesheet>
If you don't know the number and names of child elements but you want to use the troubleticketalarm with most child elements as the definition of the list then
<?xml version="1.0" encoding="UTF-8" ?>
<xsl:transform xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">
<xsl:output method="text" />
<xsl:strip-space elements="*"/>
<xsl:variable name="alarms" select="//troubleticketalarm"/>
<xsl:variable name="max" select="max($alarms/count(*))"/>
<xsl:variable name="col-names" select="$alarms[count(*) eq $max]/*/local-name()"/>
<xsl:key name="k1" match="troubleticketalarm/*" use="local-name()"/>
<xsl:template match="troubleticketalarm">
<xsl:value-of select="for $name in $col-names return (if (key('k1', $name, current())) then key('k1', $name, current()) else '')" separator="|"/>
<xsl:text>
</xsl:text>
</xsl:template>
</xsl:transform>
might help. It is online at http://xsltransform.net/gWmuiJY and outputs
3117|OSS|amo|alarmedEquipment|1|specificProblem|probableCause|2015-03-17T17:33:04|2015-03-17T17:33:04|2015-03-17T17:33:04
3118|OSS123||||||2015-03-17T17:33:0405:30||
3119|OSS124|amo|alarmedEquipment|1|||2015-03-17T17:33:0405:30||
What I understand is you are trying to concatenate the values of the node under <troubleticketalarm> and map it with one of the field (<ns:Description>) in your xslt file.
May be you can try using for-each loop and inside the loop you can concatenate the values:
<ns:Description type="Array">
<xsl:for-each select="/ticketDetails/alarms/troubleticketalarm">
<ns:Description type="String" mandatory="" readonly="">
<xsl:value-of select="concat(alarmId,'|',alarmName,'|',amo,'|',alarmedEquipment,'|',severity,'|',specificProblem,
'|',probableCause,'|',associationDate,'|',activationDate,'|',clearDate)" />
</ns:Description>
</xsl:for-each>
</ns:Description>

Can not loop through FOR XML AUTO results

I am taking some data from DB through stored procedure and i am using FOR XML AUTO to get this data.
query is like this
WHERE ITEMNUMBER=#itemid AND DATASET = #dataset FOR XML AUTO, ELEMENTS) AS filters
and result xml is
<filters extra="filters">
<ISP_WebItem>
<FILTER>Type</FILTER>
<FILTERNAME>Matematik</FILTERNAME>
<UNITCODE></UNITCODE>
</ISP_WebItem>
<ISP_WebItem>
<FILTER>Strømkilde</FILTER>
<FILTERNAME>Solceller</FILTERNAME>
<UNITCODE></UNITCODE>
</ISP_WebItem>
</filters>
Now my problem is that when i try to loop through this result in my xslt,I am unable to get any result.
My XSLT code is like this
<xsl:variable name="filt" select="msxsl:node-set(filters)"/>
<xsl:for-each select="$filt/ISP_WebItem">
<xsl:copy-of select="FILTER"/>
</xsl:for-each>
I am getting the filter xml correctly in my 'filt' variable, but the for each loop never executes correctly.
When i tried putting break points in my code,I noticed that code execution never enters inside the loop ,that means
<xsl:for-each select="filt/ISP_WebItem">
this piece of code never satisfies.
And for extra info this filter node is a part of large XML data,every other nodes in this data is working correctly(ie i can loop or do any operation with them).
can any one suggest any possible reasons.
There are two problems with your XSLT. Firstly, filt is a variable, and so you need to prefix it with a $ to indicate this, otherwise the XSLT will be looking for an element named filt
<xsl:for-each select="$filt/ISP_WebItem">
Secondly, in your XML sample, FILTER is an element, but your xsl:copy-of statement is looking for an attribute named 'FILTER'. You should be doing this
<xsl:copy-of select="FILTER"/>
However, if it is an attribute, leave it as <xsl:copy-of select="#FILTER"/>
This XSLT would work, for example
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:msxsl="urn:schemas-microsoft-com:xslt" extension-element-prefixes="msxsl">
<xsl:output method="xml" indent="yes"/>
<xsl:template match="/">
<xsl:variable name="filt" select="msxsl:node-set(filters)"/>
<xsl:for-each select="$filt/ISP_WebItem">
<xsl:copy-of select="FILTER"/>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
In fact, if you did just want to copy the elements, and nothing else, you could remove the xsl:for-each and simplify it to this
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:msxsl="urn:schemas-microsoft-com:xslt" extension-element-prefixes="msxsl">
<xsl:output method="xml" indent="yes"/>
<xsl:template match="/">
<xsl:variable name="filt" select="filters"/>
<xsl:copy-of select="$filt/ISP_WebItem/FILTER"/>
</xsl:template>
</xsl:stylesheet>
However, I am not quite sure if you need to use node-set in this instance (although I guess you are just showing an abdriged code sample, so maybe it is necessary for something elsewhere).
The following would also work
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:msxsl="urn:schemas-microsoft-com:xslt" extension-element-prefixes="msxsl">
<xsl:output method="xml" indent="yes"/>
<xsl:template match="/">
<xsl:for-each select="filters/ISP_WebItem">
<xsl:copy-of select="FILTER"/>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
EDIT: If you are still getting problems, try one of the following expessions instead.
<xsl:for-each select="$filt//ISP_WebItem">

Change a string in xml into an array in xml easily

I was wondering if anyone knew if there was a simple way to change all of my strings in my xml file to arrays?
Example:
//This is the string I need to change:
<string name="sample1">value1, value2, value3, value4, value5</string>
//This is the way I need it to be:
<array name="sample1">
<item>value1</item>
<item>value2</item>
<item>value3</item>
<item>value4</item>
<item>value5</item>
</array>
My problem is not that I do not know how to manually do it. But I am looking for a way to simulate this process more easily because I have 120 strings with 25-90 values in each.
A good example would be converting multiple images extentions with a single click using an add-on to GIMP that simulates that process for you for each image.
Would anyone who understands what I am asking, know a way to do this for string to array?
This XSLT:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
<array>
<xsl:attribute name="name">
<xsl:value-of select="string/#name"/>
</xsl:attribute>
<xsl:apply-templates/>
</array>
</xsl:template>
<xsl:template match="string/text()" name="tokenize">
<xsl:param name="text" select="."/>
<xsl:param name="sep" select="','"/>
<xsl:choose>
<xsl:when test="not(contains($text, $sep))">
<item>
<xsl:value-of select="normalize-space($text)"/>
</item>
</xsl:when>
<xsl:otherwise>
<item>
<xsl:value-of select="normalize-space(substring-before($text, $sep))"/>
</item>
<xsl:call-template name="tokenize">
<xsl:with-param name="text" select="substring-after($text, $sep)"/>
</xsl:call-template>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
applied to this XML:
<?xml version="1.0" encoding="UTF-8"?>
<string name="sample1">value1, value2, value3, value4, value5</string>
gives this output:
<?xml version="1.0" encoding="UTF-8"?>
<array name="sample1">
<item>value1</item>
<item>value2</item>
<item>value3</item>
<item>value4</item>
<item>value5</item>
</array>
The XSLT is using a recursive template going through your string values and splitting them at the comma.
This can be done with XSLT 2.0 as simply as:
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:template match="/*">
<array name="{#name}">
<xsl:for-each select="tokenize(., ',\s*')">
<item><xsl:value-of select="."/></item>
</xsl:for-each>
</array>
</xsl:template>
</xsl:stylesheet>
When this transformation is applied on the provided XML document:
<string name="sample1">value1, value2, value3, value4, value5</string>
the wanted, correct result is produced:
<array name="sample1">
<item>value1</item>
<item>value2</item>
<item>value3</item>
<item>value4</item>
<item>value5</item>
</array>

Resources