Creating dynamic array in XSLT - arrays

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>

Related

How to extract data from array in xslt 3.0?

I am trying to fetch data from array list, I'm using xslt 3.0 (saxon-HE v11.4 library) to convert json to xml in Java.
If data in array list is empty string then space should appended in the required output.
Below are required details:
sample json input:
{
"employee":{
"id":["1","2",""]
}
}
required output:
<employee>
<id>
<id indexarray="0">1</id>
<id indexarray="1">2</id>
<id indexarray="2"> </id>
</id>
<name>
<name indexarray="0">a</name>
<name indexarray="1"> </name>
<name indexarray="2"> </name>
</name>
</employee>
tried below code
<id>
<xsl:for-each select="$employee?id?*">
<xsl:choose>
<xsl:when test="$employee?id?*!=''">
<id indexarray="{position()-1}">{.}</id>
<xsl:otherwise>
<id indexarray="{position()-1}"> </id>
</xsl:otherwise>
</xsl:choose>
</xsl:for-each>
</id>
getting output as below:
<id>
<id indexarray="0">1</id>
<id indexarray="1">2</id>
<id indexarray="2"/>
</id>
Any help is appreciated.
For Saxon Java or C or CS the following should give you an idea:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="3.0"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
exclude-result-prefixes="#all"
expand-text="yes">
<xsl:output indent="yes"/>
<xsl:template match=".[. instance of map(*)]">
<Details>
<xsl:iterate select="?employee?id?*">
<name indexarray="{position() - 1}">{if (. = '') then ' ' else .}</name>
</xsl:iterate>
</Details>
</xsl:template>
</xsl:stylesheet>
SaxonJS seems to have a bug https://saxonica.plan.io/issues/5739 (at least as far as I have tested in 2.5) that somehow causes the ' ' to not being output as part of the element content.

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.

Transforming XML content to array

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

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