XSLT: How can I identify the list of values are same? - iteration

I got the following xml snippet.
<Root>
<SomeTags></SomeTags>
<Values>
<dateFrom>date 1<dateFrom>
<dateEnd>date 2<dateEnd>
<value1>10</value1>
<value2>5</value2>
</Values>
More Values here....
.....................
.....................
<Values>
<dateFrom>date n<dateFrom>
<dateEnd>date n+1<dateEnd>
<value1>10</value1>
<value2>5</value2>
</Values>
</Root>
I want to compare all <value1> values and <value2> values. If they are same, I want to compress the whole as following:
If values1 == same in all Values and If values2== same in all Values then this should the output.
<Values>
<dateFrom>date 1<dateFrom> should be from the first Values item
<dateEnd>date n+1<dateEnd> should be from the last Values item
<value1>10</value1>
<value2>5</value2>
</Values>
Else
different template.
How can i achieve this in XSLT (1.0) in BizTalk 2009 ?
I know iteration. for-each. But is there a way to break as in procedural language. Can I somehow compare each value and return a boolean saying all values are same or not.. ??
Thank you all for your responses.

This transformation:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="node()|#*">
<xsl:copy>
<xsl:apply-templates select="node()|#*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="Values">
<xsl:if test=
"not(preceding-sibling::Values
[value1=current()/value1 and value2=current()/value2])">
<Values>
<xsl:apply-templates select="value1|value2"/>
</Values>
</xsl:if>
</xsl:template>
</xsl:stylesheet>
when applied on the following well-formed XML document (produced from the severely malformed text that is provided in the question):
<t>
<Values>
<dateFrom>date 1</dateFrom>
<dateEnd>date 2</dateEnd>
<value1>10</value1>
<value2>5</value2>
</Values>
<somethingElse/>
<Values>
<dateFrom>date n</dateFrom>
<dateEnd>date n+1</dateEnd>
<value1>10</value1>
<value2>5</value2>
</Values>
</t>
produces the wanted result:
<t>
<Values>
<value1>10</value1>
<value2>5</value2>
</Values>
<somethingElse/>
</t>
Explanation:
Just applying the most fundamental design pattern of XSLT: Using and overriding the identity rule.

Related

Date comparison in XSLT using Java

I am using the below code snippet to fetch the current date in XSLT:
<xsl:variable name="currentDate">
<xsl:variable name="datePattern">yyyy-MM-dd'T'HH:mm:ss Z</xsl:variable>
<xsl:value-of select="java:format(java:java.text.SimpleDateFormat.new($datePattern), java:java.util.Date.new())" />
</xsl:variable>
Requirement is, to check if ShippedDate attribute value is equal to today's date, then include <Monitor> element, else exclude.
<Order>
<Monitors>
<Monitor ShippedDate="2015-11-10T00:00:00-05:00"/>
<Monitor ShippedDate="2015-12-03T00:00:00-05:00"/>
</Monitors>
</Order>
Can somebody please help me with what function needs to be used to achieve this?
Requirement is, to check if ShippedDate is equal to today's date, then
include <Monitor> element, else exclude.
I would suggest you do it this way:
XSLT 1.0 (+EXSLT)
<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="*"/>
<!-- identity transform -->
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="Monitors">
<xsl:copy>
<xsl:apply-templates select="Monitor[substring(#ShippedDate, 1, 10) = substring(date:date(), 1, 10)]"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
Test input:
<Order>
<Monitors>
<Monitor ShippedDate="2015-12-01T00:00:00-05:00"/>
<Monitor ShippedDate="2015-12-02T00:00:00-05:00"/>
<Monitor ShippedDate="2015-12-03T00:00:00-05:00"/>
</Monitors>
</Order>
Result, when applied on December 2, 2015:
<?xml version="1.0" encoding="UTF-8"?>
<Order>
<Monitors>
<Monitor ShippedDate="2015-12-02T00:00:00-05:00"/>
</Monitors>
</Order>
Note:
This requires a processor that supports the EXSLT date:date() extension function (e.g. Xalan or Saxon 6.5);
Only the date component is compared. If you are running the transformation in a time zone that's different from the time zone used in the input, the result may be inaccurate. If you want to also consider the time zone, then there's more work to do.
Added:
As a continuation of this, if none of the ShippedDate satisfies
condition, then no output must be transformed by XSLT.
Try:
<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:template match="/Order">
<xsl:variable name="current" select="Monitors/Monitor[substring(#ShippedDate, 1, 10) = substring(date:date(), 1, 10)]"/>
<xsl:if test="$current">
<Order>
<Monitors>
<xsl:copy-of select="$current"/>
</Monitors>
</Order>
</xsl:if>
</xsl:template>
</xsl:stylesheet>

XSLT: How to select multiple sub nodes into a single list

How can I extract sub nodes to a single list for processing in a template?
Consider the following XML.
<document>
<menu></menu>
<feature>
<header>Header 1</header>
<article>article 1</article>
<article>article 2</article>
</feature>
<feature>
<header>Heading 2</header>
<article>article 1a</article>
<article>article 2a</article>
</feature>
</document>
I'd like to extract all the article nodes to a single list for processing in a template.
I need article nodes to be available at once, because I need to do calculations based on the number of articles there are.
you can try the following stylesheet:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:ext="http://exslt.org/common"
exclude-result-prefixes="ext">
<xsl:output indent="yes" omit-xml-declaration="yes"/>
<xsl:template match="/">
<xsl:variable name="list">
<articles>
<xsl:copy-of select="descendant::article"/>
</articles>
</xsl:variable>
<xsl:variable name="vPass1" select="ext:node-set($list)"/>
<xsl:apply-templates select="$vPass1/*"/>
</xsl:template>
<xsl:template match="articles">
<xsl:copy>
<xsl:text>Number of articles: </xsl:text><xsl:value-of select="count(article)"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
when applied your input, it produces:
<articles>Number of articles: 4</articles>

Concat distinct values in a for-each

I've run into a problem and i'm looking for a quick fix. I have an xml from which i take a some values:
<root>
<item>
<property1>value</property1>
<property2>value</property2>
<property3>value</property3>
</item>
<item>...</item>
<item>...</item>
<item>...</item>
</root>
I'm making a variable to use after using:
<xsl:for-each select="root/item"><xsl:value-of select="concat(property1,';')"/></xsl:for-each>
But i've ran into a problem when too many items, the variable gets too big (over 255 characters). So i was thinking of taking only the unique values (unique property values).
Any simple way to do it ?
Thanks
Please test the stylesheet below:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="1.0">
<xsl:key name="group" match="property1" use="."/>
<xsl:variable name="unique_properties">
<xsl:for-each select="//property1[count(. | key('group', .)[1]) = 1]"><!-- this selects unique values -->
<xsl:value-of select="concat(.,';')"/>
</xsl:for-each>
</xsl:variable>
<xsl:template match="root">
<xsl:value-of select="$unique_properties"/>
</xsl:template>
</xsl:stylesheet>

How to use parameter or variable values as node name?

I am trying to use the value of a parameter or variable as a node name inside a value-of select but so far failed..
So my XML is as below.
<Data>
<Name>John Smith</Name>
<Date>28112012</Date>
<Phone>iphone</Phone>
<Car>BMW</Car>
</Data>
And my incomplete xslt looks like below.
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="2.0"
exclude-result-prefixes="#all">
<xsl:param name="nodename" select="'Name'"/>
<xsl:template match="/Data">
<Output>
<xsl:value-of select="{$nodename}"/>
</Output>
</xsl:template>
</xsl:stylesheet>
Ideally I want the out put to be
<Output>John Smith</Output>
Is there any way I can do this using XSLT?
I want to be able to select appropriate node based on a users choice.
Thanks
SK
A wild guess, let me know if it works:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0" exclude-result-prefixes="#all">
<xsl:param name="nodename" select="'Name'"/>
<xsl:template match="/Data">
<Output>
<xsl:value-of select="//*[name()=$nodename]" />
</Output>
</xsl:template>
</xsl:stylesheet>

XSLT-1.0 can a variable be used to access to other nodes?

With a simple XML like this
<value>
<num>
<accession>111</accession>
<sequence>AAA</sequence>
<score>4000</score>
</num>
</value>
I want to know if it is possible to access to a particular node from a node previously stored in a variable. The XSLT code is very short and explains better what I want to say
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes" omit-xml-declaration="yes"/>
<xsl:template match="/value/num">
<xsl:variable name="node">
<xsl:copy-of select="current()"/>
</xsl:variable>
<root>
<xsl:copy-of select="$node"/>
</root>
</xsl:template>
</xsl:stylesheet>
So I store the node in the variable "node". Then I can print the contents of the node with $node.
(EDIT) XML output
<root>
<num>
<accession>111</accession>
<sequence>AAA</sequence>
<score>4000</score>
</num>
</root>
What I want to do is to print the contents of a sub-node, like this
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes" omit-xml-declaration="yes"/>
<xsl:template match="/value/num">
<xsl:variable name="node">
<xsl:copy-of select="current()"/>
</xsl:variable>
<root>
<xsl:copy-of select="$node/accession"/>
</root>
</xsl:template>
</xsl:stylesheet>
But it gives an error (Component returned failure code: 0x80600008 [nsIXSLTProcessor.transformToFragment]) (check here)
(EDIT) The XML that I would want is
<root>
<accession>111</accession>
</root>
NOTE: The question is not how can I get this output. The question is how, using a variable as in the XSLT provided, can I get this output.
(EDIT:SOLVED)
Actually it is possible, but as pointed out in the comments, the value of a variable has to be assigned with the "select" attribute if a node-set is required. So this code was not working since the variable had a tree fragment instead of a node-set stored in it (read more information here)
Thanks!
Try this:
<xsl:transform version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes" omit-xml-declaration="yes"/>
<xsl:template match="/value">
<root>
<xsl:for-each select="num">
<xsl:variable name="node" select="current()" />
<xsl:copy-of select="$node/accession" />
</xsl:for-each>
</root>
</xsl:template>
</xsl:transform>
Note that I used xsl:transform instead of xsl:stylesheet. Also, consider using version 2.0 instead of 1.0 if you have a compliant processor, it adds a lot of useful features.
I still don't see your need for a variable, though.