Move XML element to different node using XSLT - arrays

Below is the XML Input payload. I'm looking for an xml output should have "type" element inside the each Address node. Below is the incoming request XML
<rsp:response xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:rsp="rsp.com/employee/Response/v30"
xmlns:res="res.com/Member/details/v1"
xmlns:resp="resp.com/details/v1">
<res:employee>
<resp:Employee>
<resp:FirstName>abc</resp:FirstName>
<resp:middleName></resp:middleName>
<resp:details>
<resp:Details>
<resp:type>postal</resp:type>
<resp:Addresses>
<resp:Address>
<resp:country>XYZ</resp:country>
</resp:Address>
</resp:Addresses>
</resp:Details>
<resp:Details>
<resp:type>ofc</resp:type>
<resp:Addresses>
<resp:Address>
<resp:country>XYZ</resp:country>
</resp:Address>
</resp:Addresses>
</resp:Details>
</resp:details>
</resp:Employee>
</res:employee>
</rsp:response>
Here is the XSLT used and it is not giving desired output. Using this XSLT all "type" elements is reflecting in each address block.
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:rsp="rsp.com/employee/Response/v30"
xmlns:res="res.com/Member/details/v1"
xmlns:resp="resp.com/details/v1"
version="2.0">
<xsl:output method="xml" indent="yes" omit-xml-declaration="yes"/>
<xsl:template match="node() | #*">
<xsl:copy>
<xsl:apply-templates select="node() | #*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="//*[local-name()='response']/*[local-name()='employee']/*[local-name()='Employee']/*[local-name()='details']/*[local-name()='Details']/*[local-name()='Addresses']/*[local-name()='Address']">
<xsl:copy>
<xsl:apply-templates/>
<xsl:for-each select="//*[local-name()='response']/*[local-name()='employee']/*[local-name()='Employee']/*[local-name()='details']/*[local-name()='Details']/*[local-name()='type']">
<xsl:copy-of select="."/>
</xsl:for-each>
</xsl:copy>
</xsl:template>
<xsl:template match="//*[local-name()='response']/*[local-name()='employee']/*[local-name()='Employee']/*[local-name()='details']/*[local-name()='Details']/*[local-name()='type']"/>
</xsl:stylesheet>
Desired Output XML
<rsp:response
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:rsp="rsp.com/employee/Response/v30"
xmlns:res="res.com/Member/details/v1"
xmlns:resp="resp.com/details/v1">
<res:employee>
<resp:Employee>
<resp:FirstName>abc</resp:FirstName>
<resp:middleName/>
<resp:details>
<resp:Details>
<resp:Addresses>
<resp:Address>
<resp:country>XYZ</resp:country>
<resp:type>postal</resp:type>
</resp:Address>
</resp:Addresses>
</resp:Details>
<resp:Details>
<resp:Addresses>
<resp:Address>
<resp:country>XYZ</resp:country>
<resp:type>ofc</resp:type>
</resp:Address>
</resp:Addresses>
</resp:Details>
</resp:details>
</resp:Employee>
</res:employee>
</rsp:response>

Try it this way:
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:resp="resp.com/details/v1">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:strip-space elements="*"/>
<!-- identity transform -->
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="resp:Address">
<xsl:copy>
<xsl:apply-templates/>
<xsl:copy-of select="../../resp:type"/>
</xsl:copy>
</xsl:template>
<xsl:template match="resp:type"/>
</xsl:stylesheet>
Re your attempt:
There should never be a need to use a hack like *[local-name()='type'];
You should find out what // means.

Related

XSL Iterate through list of items

I have an rss feed which is pulling from an abandoned 'Joke of the day' blog. Because the blog is no longer updating, I want to iterate through the list and display a different item each day, ideally in chronological order.
How do I identify the 'first' item in the list (the oldest post) and then show the next item each day?
The rss source is here: http://feeds.feedburner.com/DailyJokes-ACleanJokeEveryday?format=xml
The full list is here: http://dailyjokes.somelifeblog.com/
My XSL code is here, and currently displays the item at position 2, which is the most recent post:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="html"/>
<xsl:template match="/">
<xsl:apply-templates select="//item[position() < 2]"/>
</xsl:template>
<xsl:template match="item">
<content-item>
<h1><xsl:value-of select="substring-before(title, ' #Joke')"/></h1>
<p><xsl:value-of select="substring-before(description, '<a href')" disable-output-escaping="yes"/></p>
</content-item>
</xsl:template>
</xsl:stylesheet>
I am surfacing this feed in a SharePoint 2013 RSS webpart
My goal is to display a different item each day but I might settle for simple randomization. Also if anybody can suggest a free or for-cost 'Joke of the day' site which is appropriate for my work intranet, that would be appreciated!
My goal is to display a different item each day
To accomplish your goal, your processor needs to know the current date.
The following is a minimized example showing how to use the date in order to retrieve a different item every day:
XML
<rss>
<channel>
<item>
<title>Alpha</title>
</item>
<item>
<title>Bravo</title>
</item>
<item>
<title>Charlie</title>
</item>
<item>
<title>Delta</title>
</item>
<item>
<title>Echo</title>
</item>
</channel>
</rss>
XSLT 1.0
<xsl:stylesheet version="1.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:param name="current-datetime">2017-04-27T00:00:00</xsl:param>
<xsl:template match="/rss">
<xsl:variable name="JDN">
<xsl:call-template name="JDN">
<xsl:with-param name="date" select="$current-datetime"/>
</xsl:call-template>
</xsl:variable>
<xsl:variable name="n" select="count(channel/item)" />
<output>
<xsl:apply-templates select="channel/item[$JDN mod $n + 1]"/>
</output>
</xsl:template>
<xsl:template match="item">
<item>
<xsl:value-of select="title"/>
</item>
</xsl:template>
<xsl:template name="JDN">
<xsl:param name="date"/>
<xsl:param name="year" select="substring($date, 1, 4)"/>
<xsl:param name="month" select="substring($date, 6, 2)"/>
<xsl:param name="day" select="substring($date, 9, 2)"/>
<xsl:param name="a" select="floor((14 - $month) div 12)"/>
<xsl:param name="y" select="$year + 4800 - $a"/>
<xsl:param name="m" select="$month + 12*$a - 3"/>
<xsl:value-of select="$day + floor((153*$m + 2) div 5) + 365*$y + floor($y div 4) - floor($y div 100) + floor($y div 400) - 32045" />
</xsl:template>
</xsl:stylesheet>
In this example, the result will be:
<?xml version="1.0" encoding="UTF-8"?>
<output>
<item>Bravo</item>
</output>
Live demo: http://xsltransform.net/pNmBxZK
If your processor supports the EXSLT date:date-time()extension function, you can modify your stylesheet's header to:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:date="http://exslt.org/dates-and-times"
extension-element-prefixes="date">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:param name="current-datetime" select="date:date-time()"/>
For SharePoint, I am guessing you would need to use the TodayIso function in the ddwrt namespace instead.

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.

WSO2 ESB foreach function

In WSO2 ESB Proxy service, how can i iterate based on integer value from some webservice response, just like "foreach":
For example such response message:
<Response>
<noOfcustomers>10</noOfCustomers>
</Response>
I need to iterate 10 times (based on the number of customers)
Is this possible? How can i achieve this?
Thanks for your help!
I've not found a clean way to do this, but here's a messy solution.
First you need an XSLT transformation.
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" exclude-result-prefixes="xsl xsi">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:param name="iterations"/>
<xsl:template name="for.loop">
<xsl:param name="i"/>
<xsl:param name="count"/>
<!--begin_: Line_by_Line_Output -->
<xsl:if test="$i <= $count">
<iteration>
<xsl:value-of select="$i"/>
</iteration>
</xsl:if>
<!--begin_: RepeatTheLoopUntilFinished-->
<xsl:if test="$i <= $count">
<xsl:call-template name="for.loop">
<xsl:with-param name="i">
<xsl:value-of select="$i + 1"/>
</xsl:with-param>
<xsl:with-param name="count">
<xsl:value-of select="$count"/>
</xsl:with-param>
</xsl:call-template>
</xsl:if>
</xsl:template>
<xsl:template match="/">
<iterations>
<xsl:call-template name="for.loop">
<xsl:with-param name="i">1</xsl:with-param>
<xsl:with-param name="count"><xsl:value-of select="$iterations"/></xsl:with-param>
</xsl:call-template>
</iterations>
</xsl:template>
</xsl:stylesheet>
Then you use the transformation in your sequence like this:
<inSequence>
<xslt key="conf:/repository/test/iterations.xslt">
<property name="iterations" expression="//noOfcustomers"/>
</xslt>
<iterate expression="//iterations/iteration" sequential="true">
<target>
<sequence>
</sequence>
</target>
</iterate>
</inSequence>
The sequence in the iterate mediator will run for each element in "iterations". The drawback to this approach is that you are replacing the message body with the iteration XML, so you have to use the enrich meditor before the transformation to save the original message to a property if you wish to reuse it.
You can iterate based on xpath. But currently we don't have counter support. What is your actual usecase?
This is supported with ForEach mediator from ESB 4.9 onwards

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