473,395 Members | 1,348 Online
Bytes | Software Development & Data Engineering Community
Post Job

Home Posts Topics Members FAQ

Join Bytes to post your question to a community of 473,395 software developers and data experts.

XSLT compare attributes of XML nodes

Hi all,
I am trying to merge 2 XML files that first of all i need to compare nodes of both files according to 2 attributes in the nodes. If those 3 attributes are equal, i need to replace the existing node with the new one. For example first xml file will be like;

Expand|Select|Wrap|Line Numbers
  1. <Root>
  2.           <Node Id ="1" FormId="form1" Value="value1"/>
  3.           <Node Id ="2" FormId="form1" Value="value2"/>
  4.           <Node Id ="3" FormId="form1" Value="value3"/>
  5. </Root>
and second xml file will be like;

Expand|Select|Wrap|Line Numbers
  1. <Root>
  2.           <Node Id ="1" FormId="form1" Value="NewValue1"/>
  3.           <Node Id ="2" FormId="form1" Value="NewValue2"/>
  4.           <Node Id ="4" FormId="form1" Value="NewValue4"/>
  5. </Root>
and when i merged these 2 files result should be;

Expand|Select|Wrap|Line Numbers
  1. <Root>
  2.           <Node Id ="1" FormId="form1" Value="NewValue1"/>
  3.           <Node Id ="2" FormId="form1" Value="NewValue2"/>
  4.           <Node Id ="3" FormId="form1" Value="value3"/>
  5.           <Node Id ="4" FormId="form1" Value="NewValue4"/>
  6. </Root>
In those files, i compare the nodes of the second file with the nodes of the first file according to "Id" and "FormId" attributes. If those attributes are equal, that node in the first file will be replaced with the node in the second file. If they are not equal, current node in the second file will be written in the first file. What i have done so far is merging two xml files by comparing the distinct nodes according to all attributes in the nodes. But i couldn't manage only comparing nodes according to "Id" and "FormId" attributes. I am using that XSLT code to compare the equalty of the nodes and it works fine.

Expand|Select|Wrap|Line Numbers
  1. <xsl:for-each select="$node1/@*">       
  2.             <xsl:if test="not($node2/@* [local-name()=local-name(current()) and namespace-uri()=namespace-uri(current()) and .=current()])">.</xsl:if>
  3. </xsl:for-each>
When i changed that code only to compare according to those 2 attributes it doesn't work.

Expand|Select|Wrap|Line Numbers
  1. <xsl:for-each select="$node1/@*">
  2.             <xsl:if test="not($node2/@* [@Id = current()/@Id and @FormId = current()/@FormId])">.</xsl:if>
  3. </xsl:for-each>
Any help would be appreciated.
Nov 17 '08 #1
12 18209
8,658 Expert Mod 8TB
you put in one attribute node too many. use $node2 instead of $node2/@*. 1)

a more elagant way would be without the if clause
Expand|Select|Wrap|Line Numbers
  1. <xsl:for-each select="$node1[@Id != $node2/@Id][@FormId != $node2/@FormId]">
  2. // code goes here for every unique $node1
  3. </xsl:for-each>

Expand|Select|Wrap|Line Numbers
  1. // your first condition
  2. $node2/@* [@Id = current()/@Id]
  3. // resolves to
  4. $node2/@*/@Id = $node1/@*/@Id
  5. // which returns true (both are empty/invalid) ...
Nov 17 '08 #2
Dormilich, thanks for your reply,
I tried what you recommended like the following;

Expand|Select|Wrap|Line Numbers
  1. <xsl:for-each select="$node1[@Id != $node2/@Id][@FormId != $node2/@FormId]">
  2.             <xsl:if test="not($node2/@*/@Id = $node1/@*/@Id and $node2/@*/@FormId = $node1/@*/@FormId)">.</xsl:if>
  3. </xsl:for-each>
But it still gives the same output. What am i doing wrong?
Nov 17 '08 #3
8,658 Expert Mod 8TB
you misunderstood. my code doesn't require <xsl:if>. the for-each loop should select all unique elements of $node1 (so that you can copy them).


Expand|Select|Wrap|Line Numbers
  1. <xsl:if test="not($node2/@*/@Id = $node1/@*/@Id and $node2/@*/@FormId = $node1/@*/@FormId)">.</xsl:if>
will always fail since there are no elements who fulfill this condition.

Expand|Select|Wrap|Line Numbers
  1. $node2/@*/@Id -> empty
  2. $node1/@*/@Id -> empty
  3. empty = empty -> true
  4. not(true) -> false -> if statement skipped
Nov 17 '08 #4
Sorry for misunderstanding. My XSLT code has two parts. First part compares the nodes and second part copies the nodes according to the results of comparation. When comparing if required attributes are equal, i need to get a "=" and after that update the node. If attributes are not equal, i need to get a "!" and then copy the node from second file to the first file. Below is the comparation part of the code.

Expand|Select|Wrap|Line Numbers
  1. <!-- 
  2.  Comparing single nodes: 
  3.      if $node1 and $node2 are equivalent then the template creates a 
  4.      text node "=" otherwise a text node "!" -->
  5.      <xsl:template name="m:compare-nodes">
  6.       <xsl:param name="node1" />
  7.       <xsl:param name="node2" />
  8.        <xsl:variable name="type1">
  9.         <xsl:apply-templates mode="m:detect-type" select="$node1" />
  10.       </xsl:variable>
  11.        <xsl:variable name="type2">
  12.         <xsl:apply-templates mode="m:detect-type" select="$node2" />
  13.       </xsl:variable>
  14.        <xsl:choose>
  15.          <!--  Are $node1 and $node2 element nodes with the same name? -->
  16.          <xsl:when test="$type1='element' and $type2='element' and local-name($node1)=local-name($node2) and namespace-uri($node1)=namespace-uri($node2) and name($node1)!=$dontmerge and name($node2)!=$dontmerge">
  17.            <!--  Comparing the attributes -->
  18.            <xsl:variable name="diff-att">
  19.              <!--  same number ... -->
  20.             <xsl:if test="count($node1/@*)!=count($node2/@*)">.</xsl:if>
  21.              <!--  ... and same name/content -->
  22.              <xsl:for-each select="$node1/@*">
  23.              <!--<xsl:for-each select="$node1/Caption">-->
  24.                <xsl:value-of select="@Id" />
  25.                <xsl:value-of select="@FormId" />
  26.                <xsl:value-of select="@LangId" />
  27.                <!--<xsl:if test="not($node2/Caption/Id/FormId/LangId [local-name()=local-name(current()) and namespace-uri()=namespace-uri(current()) and .=current()])">.</xsl:if>-->
  28.                <xsl:if test="not($node2/@* [local-name()=local-name(current()) and namespace-uri()=namespace-uri(current()) and .=current()])">.</xsl:if>
  29.             </xsl:for-each>
  30.           </xsl:variable>
  31.            <xsl:choose>
  32.             <xsl:when test="string-length($diff-att)!=0">!</xsl:when>
  33.             <xsl:otherwise>=</xsl:otherwise>
  34.           </xsl:choose>
  35.         </xsl:when>
  36.          <!--  Other nodes: test for the same type and content -->
  37.         <xsl:when test="$type1!='element' and $type1=$type2 and name($node1)=name($node2) and ($node1=$node2 or ($normalize='yes' and normalize-space($node1)= normalize-space($node2)))">=</xsl:when>
  38.          <!--  Otherwise: different node types or different name/content -->
  39.         <xsl:otherwise>!</xsl:otherwise>
  40.       </xsl:choose>
  41.     </xsl:template>
Sorry i don't know much about XSLT so i have difficulty in solving the problem.

Nov 17 '08 #5
Anybody knows how to fix that??
Nov 17 '08 #6
8,658 Expert Mod 8TB
if you just (well...) want to merge two files, why making it by the means of a comparison string? Isn't that complicated (well, it looks so...)?

As I (hopefully) understand you right you want to merge two xml files omitting duplicate nodes (regarding the attributes) from the first xml. Do you need "!" and "=" in the output too?

I'd do a straightforward merging.

the idea is to copy all nodes from xml no.2 and add all nodes from xml no.1 that are not present in xml no.2.

like (sketch)
Expand|Select|Wrap|Line Numbers
  1. // select mergable nodes
  2. <xsl:variable name="node1" select="//*[self::name() != $dontmerge]"/>
  3. //... there may be more conditions depending on the xml
  5. <xsl:template match="/">
  6.   <xsl:apply-templates/>
  7. </xsl:template>
  9. // add xml no.2
  10. <xsl:template match="$node2">
  11.   <xsl:copy-of select="."/> // or whatever value you need
  12. </xsl:template>
  14. // add xml no.1 using conditions
  15. <xsl:template match="$node1[@Id != $node2/@Id][@FormId != $node2/@FormId]">
  16.   <xsl:copy-of select="."/> // or whatever value you need
  17. </xsl:template>
this idea should at least work for your example....

Nov 17 '08 #7
Thanks again for your reply. In your code i can only insert the nodes which doesn't exist in the file1. But i also want to replace the nodes having the same "Id" and "FormId" attribute values. For ex. if i have a line in file1 like;

Expand|Select|Wrap|Line Numbers
  1. <Node Id="1" FormId="form1" Values="value1"> 
and a line in file2 like;

Expand|Select|Wrap|Line Numbers
  1. <Node Id="1" FormId="form1" Values="new Value"> 
which both nodes have same "Id" and "FormId" attribute values("1" and "form1") and corresponding output will be;

Expand|Select|Wrap|Line Numbers
  1. <Node Id="1" FormId="form1" Values="new Value"> 
that first node will be replaced with second node. So i have two options according to what i get from the comparation("!" or "="). If result is "!" i will insert the node from the file2 to file1, and if result is "=" i will replace the node(as in the example above). So is it possible to modify the code i give to get a result "!" or "=" according to the values of "Id" and "FormId" attributes.
Hope it is obvious to understand.

Nov 17 '08 #8
2,057 Expert 2GB
Sorry if I'm misinterpreting (my mind isn't working that well yet today).
Are you sorting based on id? Is it possible to have the same id, but different FormId?

Would have suggested a 1-liner:
<xsl:copy-of select="$node1[not ($node2[@Id = current()/@Id])] | $node2"/>

or if order matters, change to a for-each:
Expand|Select|Wrap|Line Numbers
  1. <xsl:for-each select="$node1[not ($node2[@Id = current()/@Id])] | $node2">
  2.   <xsl:sort select="@Id" order="ascending"/>
  3.   <xsl:copy-of select="."/>
  4. </xsl:for-each>
Otherwise, I agree with Dormilich about the xpath being wrong. (attributes don't have children attributes)

I think we're going about the same problem 2 different ways.
If we're going to 'update' the old node with new values, why not just copy the new node?
Nov 17 '08 #9
Of course it may have the same Id and different FormId and i can already copy the node in the merge part. And in the xml files attributes don't have child attributes(i know it is impossible). Just each node has attributes ("Id", "FormId", "Values"). Merging part of the XSLT file can copy the nodes correctly. But when i comparing the nodes("m:compare-nodes" template i sent in one of the messages above) i always get a "!" that shows nodes are not equal(eventhough "Id" and "FormId" attribute values are equal). I only want to get the correct output ("!" or "=") from the compare template. By the way i don't need to sort the nodes. Here i need to modify the above "m:compare-nodes" template.

Nov 17 '08 #10
2,057 Expert 2GB
Problem here:

. = '' (empty string)
current() = '' (empty string)

Of course you are going to always get false.

Further, you can reduce
local-name() = local-name(current()) and namespace-uri() = namespace-uri(current())
to name() = name(current())

The solution you provide seems overcomplicated; don't know if there's a specific reason why you're doing it this way.
Nov 17 '08 #11
Thank you all for your helps. But result is still same. Only if nodes are totally equal, they are not inserted into the file. Otherwise all nodes(even having same "Id" and "FormId" which i want those nodes to be evaluated as equal and then replace those nodes in the file1 with the nodes in the file2) are inserted.
Nov 18 '08 #12
8,658 Expert Mod 8TB
there seems to be a bit of confusion about how to replace the nodes.

there are basicly two points of view:
1) ("forward view") get the nodes of xml 1 and replace the duplicate ones by the corresponding nodes of xml 2 + adding the unique nodes of xml 2
2) ("backward view") get the nodes of xml 2 and insert the unique nodes of xml 1

personally, I favour option 2, because (as I understand it) all nodes of xml 2 will show up in the merged xml.

Nov 18 '08 #13

Sign in to post your reply or Sign up for a free account.

Similar topics

by: Iain | last post by:
I want to create an XML configuration file which might look like <REGION Name="Europe" WingDing="Blue"> <COUNTRY Name="UK" WingDing="white"> <TOWN Name="London" WingDing="Orange" /> </COUNTRY>...
by: inquirydog | last post by:
Hi- Does anyone know a way to compare whether two nodes contain the same information in xslt (the name, attributes, and all content recursivly should be the same. I am interested in the case...
by: Jon Martin Solaas | last post by:
Hi, I have a general document somewhat like this: -------------------------------------- <root> <level1> <level2> <interestingstuff number="2"/> <interestingstuff number="3"/>...
by: Stephan Brunner | last post by:
Hi I have created two flavors of an XSLT stylesheet to transform all attributes of an XML document to elements: They both work as expected with MSXML and XMLSPY but throw an exception ...
by: Alex | last post by:
I stumbled upon this while developing a custom XPathNavigator. It appears that copy action for attributes is broken in the .net framework XSLT processor. The intent was to just copy the entities...
by: Ian Roddis | last post by:
Hello, I want to embed SQL type queries within an XML data record. The XML looks something like this: <DISPLAYPAGE> <FIELD NAME="SERVER" TYPE="DROPDOWN"> <OPTION>1<OPTION> <OPTION>2<OPTION>...
by: Foxpointe | last post by:
Given some arbitrary XHTML, I'd like to obtain a 'simplified' XHTML result which strips out a large subset of standard elements and attributes - but not all. The main things I would like to...
by: byquestion | last post by:
Hi there xslt gurus, i am kinda new to xslt and having difficulty to implement my some iterations. i need to recreate an xml file by using xslt. here is the sample xml file(input for xslt) ...
by: ryjfgjl | last post by:
If we have dozens or hundreds of excel to import into the database, if we use the excel import function provided by database editors such as navicat, it will be extremely tedious and time-consuming...
by: ryjfgjl | last post by:
In our work, we often receive Excel tables with data in the same format. If we want to analyze these data, it can be difficult to analyze them because the data is spread across multiple Excel files...
by: BarryA | last post by:
What are the essential steps and strategies outlined in the Data Structures and Algorithms (DSA) roadmap for aspiring data scientists? How can individuals effectively utilize this roadmap to progress...
by: nemocccc | last post by:
hello, everyone, I want to develop a software for my android phone for daily needs, any suggestions?
by: Sonnysonu | last post by:
This is the data of csv file 1 2 3 1 2 3 1 2 3 1 2 3 2 3 2 3 3 the lengths should be different i have to store the data by column-wise with in the specific length. suppose the i have to...
by: Hystou | last post by:
There are some requirements for setting up RAID: 1. The motherboard and BIOS support RAID configuration. 2. The motherboard has 2 or more available SATA protocol SSD/HDD slots (including MSATA, M.2...
by: marktang | last post by:
ONU (Optical Network Unit) is one of the key components for providing high-speed Internet services. Its primary function is to act as an endpoint device located at the user's premises. However,...
by: Hystou | last post by:
Most computers default to English, but sometimes we require a different language, especially when relocating. Forgot to request a specific language before your computer shipped? No problem! You can...
by: Oralloy | last post by:
Hello folks, I am unable to find appropriate documentation on the type promotion of bit-fields when using the generalised comparison operator "<=>". The problem is that using the GNU compilers,...

By using Bytes.com and it's services, you agree to our Privacy Policy and Terms of Use.

To disable or enable advertisements and analytics tracking please visit the manage ads & tracking page.