XSLT - Sum all the attribute values - xslt

Need to sum all the attribute value without actually hard coding the attribute names. Below is the example having 2 attributes. The scenario is that there could be n number of attributes which is not known before hand.
<Books>
<Book a="10" b="20" />
<Book a="30" b="40" />
</Books>
Output:
<analysis>
<a>40</a>
<b>60</b>
</analysis>

It's a simple exercise in grouping:
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:template match="/Books">
<analysis>
<xsl:for-each-group select="Book/#*" group-by="name()">
<xsl:element name="{name()}">
<xsl:value-of select="sum(current-group())" />
</xsl:element>
</xsl:for-each-group>
</analysis>
</xsl:template>
</xsl:stylesheet>

Related

XSLT: Copy an element with all attributes where an attribute matches a value

Given an XML structure like follows:
<Resources PossibleAttribute="SomethingHere">
<Book ID="1" OtherAttribute="abc" />
<Book ID="2" DifferentAttribute="def" />
</Resources>
I need to copy the structure and element where ID='1'. So I'm looking to end up with:
<Resources PossibleAttribute="SomethingHere">
<Book ID="1" OtherAttribute="abc" />
</Resources>
What I've come up with thus far is:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes"/>
<xsl:template match="Resources">
<xsl:copy>
<xsl:apply-templates select="#*|node()" />
</xsl:copy>
</xsl:template>
<xsl:template match="Book[#ID='1']">
<xsl:copy>
<xsl:value-of select="#*|node()" />
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
This, however, is not producing the intended end result, giving me this instead:
<Resources>
<Book>1</Book>
</Resources>
A key item here is that any of these elements may contain additional attributes that are not specified here or currently known. Hence, if additional attributes are present, they too should be copied.
Any tips you can offer would be most appreciated!
Why not simply:
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:template match="/Resources">
<xsl:copy>
<xsl:copy-of select="#*"/>
<xsl:copy-of select="Book[#ID=1]"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>

How to perform intersection in nodeset using xpath 2.0

I have two different node-sets that contain one or more elements with the same name.
I want to select these same-named elements by using the intersect operation in XPath 2.0 and XPath 1.0 as well.
This is the sample code i have tried.
Input:
<?xml version="1.0" encoding="UTF-8"?>
<root>
<child1>
<a />
<b />
<d />
</child1>
<child2>
<c />
<a />
<d />
</child2>
</root>
The code I tried
Xpath 1.0:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
<!-- TODO: Auto-generated template -->
<xsl:variable name="ns2" select="/root/child2/child::*"/>
<xsl:copy-of select="/root/child1/child[.=$ns2]"/>
</xsl:template>
</xsl:stylesheet>
Xpath 2.0:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
<!-- TODO: Auto-generated template -->
<xsl:variable name="ns1" select="/root/child1/child::*"/>
<xsl:variable name="ns2" select="/root/child2/child::*"/>
<xsl:copy-of select="$ns1 intersect $ns2"/>
</xsl:template>
</xsl:stylesheet>
Issue: I am getting the empty result.
Expected result:
<?xml version="1.0" encoding="UTF-8"?>
<a/>
<d/>
Please suggest what I am missing.
I have tried the set operation intersection for two different noteset.I have attached my code sample below,
You are not really doing "intersection" here, because all of the child elements of child1 are different to the child elements of child2. Just because two elements share the same name, that does not make them the same element.
What it looks like you are after is elements under child1 that have the same name as an element under child2
If you want your XSLT 1.0 solution to get results, you need to change the xsl:copy-of to this
<xsl:copy-of select="/root/child1/child::*[.=$ns2]"/>
Or this...
<xsl:copy-of select="/root/child1/*[.=$ns2]"/>
However, this will return all child1 child elements, because you are checking the value of the elements are the same, not the name. One way to do it is this...
<xsl:for-each select="/root/child1/*">
<xsl:variable name="name" select="local-name()" />
<xsl:copy-of select=".[$ns2[local-name() = $name]]" />
</xsl:for-each>
Alternatively, define a key like so:
<xsl:key name="child2" match="child2/*" use="local-name()" />
Then you could do this...
<xsl:copy-of select="/root/child1/*[key('child2', local-name())]"/>
In XSLT 2.0, you could do something like this....
<xsl:copy-of select="$ns1[some $name in $ns2/local-name() satisfies $name=local-name()]"/>
Not repeating Tim's correct remark, but just to the problem:
XSLT 1.0 (Not possible with a single XPath expression, unless an XSLT standard function, such as current() is used):
<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="child1/*">
<xsl:copy-of select="self::*[../../child2/*[name() = name(current())]]"/>
</xsl:template>
</xsl:stylesheet>
XPath 2.0:
Use:
/*/child1/*[name() = /*/child2/*/name()]
This can be verified with the below XSLT 2.0 transformation which evaluates the expression and outputs the selected nodes:
<xsl:stylesheet version="2.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="/">
<xsl:sequence select=
"/*/child1/*[name() = /*/child2/*/name()]"/>
</xsl:template>
</xsl:stylesheet>
When applied on the provided XML document:
<root>
<child1>
<a />
<b />
<d />
</child1>
<child2>
<c />
<a />
<d />
</child2>
</root>
both transformations produce the wanted, correct result:
<a/>
<d/>

Transforming XML Attributes into XML Elements

I have the following XML input:
<?xml version="1.0" encoding="utf-8"?>
<DOCUMENT>
<EXTERNALFILES>
<PRIMARYFILE FileName="C:\MSSB\POC\Exports\13f78581-1501-4dd0-8cf3-a5a300ba4083\121110-26-2001031100092.TIF" />
</EXTERNALFILES>
<OCRTEXTFILES />
<DOCUMENTINDEX Name="A2iA_CheckAmount">3304.49</DOCUMENTINDEX>
<DOCUMENTINDEX Name="A2iA_CheckCAR">3304.49</DOCUMENTINDEX>
<DOCUMENTINDEX Name="A2iA_CheckCodeline_OnUs1">28557833</DOCUMENTINDEX>
<DOCUMENTINDEX Name="A2iA_CheckCodeline_Transit">031100092</DOCUMENTINDEX>
<DOCUMENTINDEX Name="A2iA_CheckDate">10-26-2001</DOCUMENTINDEX>
<DOCUMENTINDEX Name="A2iA_CheckLAR">3304.49</DOCUMENTINDEX>
<DOCUMENTINDEX Name="A2iA_CheckNumber">1211</DOCUMENTINDEX>
<DOCUMENTINDEX Name="A2iA_CheckPayeeName">BCBSD</DOCUMENTINDEX>
<DOCUMENTINDEX Name="CheckPayerName">SOFTPRO NORTH AMERICA, INC.</DOCUMENTINDEX>
</DOCUMENT>
I need to transform this into the following output:
<?xml version="1.0" encoding="utf-8"?>
<DOCUMENT>
<A2iA_CheckAmount>3304.49</A2iA_CheckAmount>
<A2iA_CheckCAR>3304.49</A2iA_CheckCAR>
<A2iA_CheckLAR>3304.49</A2iA_CheckLAR>
<A2iA_CheckAccountNumber>28557833</A2iA_CheckAccountNumber>
<A2iA_CheckRoutingNumber>031100092</A2iA_CheckRoutingNumber>
<A2iA_CheckNumber>1211</A2iA_CheckNumber>
<A2iA_CheckAmount>3304.49</A2iA_CheckAmount>
<A2iA_CheckPayeeName>BCBSD</A2iA_CheckPayeeName>
<CheckPayerName>SOFTPRO NORTH AMERICA, INC</CheckPayerName>
</DOCUMENT>
I am using the following XSLT code:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes"/>
<xsl:value-of select="concat('<DOCUMENT>')"/>
<xsl:template match="/*">
<xsl:for-each select="DOCUMENT/DOCUMENTINDEX/#*">
<xsl:value-of select="concat('<', name(), '>', ., '</', name(), '>')"/>
</xsl:for-each>
<xsl:value-of select="concat('</DOCUMENT>')"/>
</xsl:template>
I tested this with an online XSLT Tester and it threw a non-descriptive error. I am new to XSLT Transformation. Any help will be greatly appreciated. Thank You.
One possibility to achieve this is the following simple one(EDITED):
<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:template match="/DOCUMENT">
<xsl:element name="Document">
<xsl:apply-templates />
</xsl:element>
</xsl:template>
<xsl:template match="DOCUMENTINDEX">
<xsl:element name="{#Name}">
<xsl:value-of select="text()" />
</xsl:element>
</xsl:template>
</xsl:stylesheet>
It gives the output (EDITED)
<Document>
<A2iA_CheckAmount>3304.49</A2iA_CheckAmount>
<A2iA_CheckCAR>3304.49</A2iA_CheckCAR>
<A2iA_CheckCodeline_OnUs1>28557833</A2iA_CheckCodeline_OnUs1>
<A2iA_CheckCodeline_Transit>031100092</A2iA_CheckCodeline_Transit>
<A2iA_CheckDate>10-26-2001</A2iA_CheckDate>
<A2iA_CheckLAR>3304.49</A2iA_CheckLAR>
<A2iA_CheckNumber>1211</A2iA_CheckNumber>
<A2iA_CheckPayeeName>BCBSD</A2iA_CheckPayeeName>
<CheckPayerName>SOFTPRO NORTH AMERICA, INC.</CheckPayerName>
</Document>

Identity Transformation - compare attributes and limit output

I want to compare the attributes of two xml-Files and identity transform the input file in the same step. The output xml should only contain elements whose attributes occur in the comparing xml. As shown in the given example, the last concept node should not be outputted, as there is no matching attribute in the comparing.xml
input.xml
<navigation
xmlns:fo="http://www.w3.org/1999/XSL/Format"
xmlns:xs="http://www.w3.org/2001/XMLSchema">
<facets>
<facet id="d1e12000000000000000000000011111">
<title xml:lang="en">sometxt</title>
<title xml:lang="de">eintxt</title>
<concepts>
<concept id="d1e12000000000000000000000000000">
<title xml:lang="en">sometxt</title>
<title xml:lang="de">eintxt</title>
<concepts>
<concept id="d1e19000000000000000000000000000">
<title xml:lang="en">sometxt</title>
<title xml:lang="de">eintxt</title>
<concepts>
</concepts>
</concept>
</concepts>
</concept>
</concepts>
</facet>
</facets>
part of comparing.xml with indefinite heading-levels
<foo>
<heading class="d1e12000000000000000000000011111|d1e12000000000000000000000000000">Myheading</heading>
<chapter>
<heading class="d1e12000000000000000000000011111|d1e12000000000000000000000000000">myheading</heading>
<operation>
<heading class="d1e12000000000000000000000011111|d1e12000000000000000000000000000">another heading</heading>
</operation>
</chapter>
desired output.xml with only applicable id's
<nav:navigation
xmlns:fo="http://www.w3.org/1999/XSL/Format"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:nav="http://www.nav.de/">
<nav:facets>
<nav:facet id="d1e12000000000000000000000011111">
<nav:title xml:lang="en">sometxt</nav:title>
<nav:title xml:lang="de">eintxt</nav:title>
<nav:concepts>
<nav:concept id="d1e12000000000000000000000000000">
<nav:title xml:lang="en">sometxt</nav:title>
<nav:title xml:lang="de">eintxt</nav:title>
<nav:concepts>
</nav:concepts>
</nav:concept>
</nav:concepts>
</nav:facet>
</nav:facets>
my xsl so far
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:fo="http://www.w3.org/1999/XSL/Format"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:nav="http://www.nav.de/"
version="2.0" >
<xsl:output method="xml" indent="yes" encoding="utf-8"/>
<xsl:variable name="docu" select="document(comparing.xml)"/>
<xsl:template match="*">
<xsl:element name="nav:{name()}" namespace="http://www.nav.de/">
<xsl:copy-of select="namespace::*"/>
<xsl:apply-templates select="node()|#*"/>
</xsl:element>
</xsl:template>
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
EDIT: sorry for posting this in the comment-section. I've tried something along those lines, but it didn't work
<xsl:template match="concept | facet">
<xsl:variable name="foo-id" select="#id"/>
<xsl:for-each select="$docu//heading">
<xsl:if test="contains(./#class, $foo-id)">
<xsl:apply-templates/>
</xsl:if>
</xsl:for-each>
</xsl:template>
I would suggest you try it this way:
XSLT 2.0
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:nav="http://www.nav.de/">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:param name="comparing-url" select="'comparing.xml'"/>
<xsl:key name="comp" match="#class" use="tokenize(., '\|')" />
<xsl:template match="*">
<xsl:element name="nav:{name()}" >
<xsl:copy-of select="#*"/>
<xsl:apply-templates/>
</xsl:element>
</xsl:template>
<xsl:template match="*[#id][not(key('comp', #id, document($comparing-url)))]"/>
</xsl:stylesheet>

XSLT XPath, Need to change node value to based on number value of a sibling node

I am trying to change a node value to one of two different things by checking to see if the the following node contains an odd or even number.
This is the source XML.
<?xml version="1.0" encoding="UTF-8"?>
<FILE>
<CLAIM>
<PERSON>
<PROVIDER>
<HEADER>
<FLAG>PPON</FLAG>
<IDNO>11612</IDNO>
</HEADER>
</PROVIDER>
</PERSON>
</CLAIM>
</FILE>
And the XSLT I'm trying is:
<?xml version='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"
omit-xml-declaration="no"/>
<xsl:strip-space elements="*"/>
<xsl:template match="node()|#*">
<xsl:copy>
<xsl:apply-templates select="node()|#*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="FLAG/node()">
<xsl:choose>
<xsl:when test="number(../IDNO) mod 2 = 0">EVEN</xsl:when>
<xsl:otherwise>ODD</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
And what I want to the output to be is
<?xml version="1.0" encoding="UTF-8"?>
<FILE>
<CLAIM>
<PERSON>
<PROVIDER>
<HEADER>
<FLAG>EVEN</FLAG>
<IDNO>11612</IDNO>
</HEADER>
</PROVIDER>
</PERSON>
</CLAIM>
</FILE>
I know I'm not getting the IDNO in my when test because my code gives me ODD all the time, also while debugging I tried putting the value of the when test into FLAG and I get NaN. But I can't come up with the correct syntax to get the IDNO into the test.
And yes, I'm new to XSLT, so maybe this is a dumb question, but I've tried many different things and searched this site and others for the answer with no luck.
Thanks in advance for your help.
DWL
You current template matches on a child of FLAG
<xsl:template match="FLAG/node()">
Now, when you use .. this is looking for the parent, so .. will match FLAG in this context, and so ../IDNO is looking for a child of FLAG called IDNO, which does not exist.
You need to go one more level up. Try this instead
<xsl:template match="FLAG/text()">
<xsl:choose>
<xsl:when test="number(../../IDNO) mod 2 = 0">EVEN</xsl:when>
<xsl:otherwise>ODD</xsl:otherwise>
</xsl:choose>
</xsl:template>
It might be easier to not check the axis of the node, but rather the node itself:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:a="http://www.mycompany.com/services/core/file" exclude-result-prefixes="a">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes" omit-xml-declaration="no"/>
<xsl:strip-space elements="*"/>
<xsl:template match="/">
<xsl:apply-templates/>
</xsl:template>
<xsl:template match="*">
<xsl:copy>
<xsl:copy-of select="#*"/>
<xsl:apply-templates/>
</xsl:copy>
</xsl:template>
<xsl:template match="IDNO">
<xsl:copy>
<xsl:copy-of select="#*"/>
<xsl:choose>
<xsl:when test="number(.) mod 2 = 0">EVEN</xsl:when>
<xsl:otherwise>ODD</xsl:otherwise>
</xsl:choose>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>