3

Suppose I have an A document like this:

<document>
    <element>
        <value>1</value>
        <wom>bat</wom>
    </element>
    <bar>
        <baz />
        <baz />
        <baz />
    </bar>
</document>

and a B document like this:

<document>
    <element>
        <value>2</value>
    </element>
    <bar>

    </bar>
</document>

With the result which looks like this:

<document>
    <element>
        <value>2</value>
        <wom>bat</wom>
    </element>
    <bar>

    </bar>
</document>

So what I'd like to achieve is to overwrite values in a tag (like in element) in document A with values provided from document B but leave the sibling values untouched. If the tag in B however is empty (leaf) I want its counterpart in A to be emptied as well. I've checked this question but it is merging not overwriting. How can I solve this problem?

Clarification: A and B documents have the same structure but B has less elements. I have to empty every element in A which is empty in B and I have to overwrite every inner element in an element if it is not empty (see my example).

Community
  • 1
  • 1
Adam Arold
  • 29,285
  • 22
  • 112
  • 207
  • 1
    That's a very wide question. Do you have **specific** elements (with known names and paths) you want to compare? – michael.hor257k Jun 06 '14 at 14:50
  • Every element needs to be merged. There are only 2 rules as I've specified. – Adam Arold Jun 06 '14 at 15:15
  • Well, that's not going to be simple, unless elements have unique names. Otherwise you'll need to match each element with its counterpart using the entire path. Are you using XSLT 2.0? – michael.hor257k Jun 06 '14 at 15:26
  • You could use something like [this stylesheet](http://www2.informatik.hu-berlin.de/~obecker/XSLT/#merge) which merges documents with any structure and add a template to simply copy the nodes that are empty in document B (`*[not(normalize-space(child::node()))]`) ignoring their contents in document A. – helderdarocha Jun 06 '14 at 16:15
  • I can use whatever tool which solves my problem. – Adam Arold Jun 06 '14 at 22:21

1 Answers1

5

One approach could be to navigate over DocumentA, but passing in a parameter set to the equivalent node in Document B.

To start with, match the document node of A, and start the matching off with the document node from B

   <xsl:template match="/">
      <xsl:apply-templates>
         <xsl:with-param name="parentB" select="document('DocB.xml')"/>
      </xsl:apply-templates>
   </xsl:template>

Then, you would have a template matching any element (in A) with the current (parent) node in B as the parameter

   <xsl:template match="*">
      <xsl:param name="parentB"/>

To find the equivalent 'child' node in B, you would first find the current position of the A node (should there be more than one child of the same name), and then check if such a child exists under the parent B node

<xsl:variable name="posA">
   <xsl:number  />
</xsl:variable>
<xsl:variable name="nodeB" select="$parentB/*[local-name() = local-name(current())][number($posA)]"/>

Then, it is just a case of determining whether to copy the A or B node. To copy the B node, the B node would have to exist, and not have any child elements (it might have child text nodes though, which would be copied

<xsl:when test="$nodeB and not($nodeB/*)">
   <xsl:copy-of select="$nodeB/node()"/>
</xsl:when>

Otherwise, continue processing the A node (passing in the current B node as a parameter).

Try this XSLT

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
   <xsl:output method="xml" indent="yes"/>

   <xsl:template match="/">
      <xsl:apply-templates>
         <xsl:with-param name="parentB" select="document('DocB.xml')"/>
      </xsl:apply-templates>
   </xsl:template>

   <xsl:template match="*">
      <xsl:param name="parentB"/>
      <xsl:variable name="posA">
          <xsl:number  />
       </xsl:variable>
      <xsl:variable name="nodeB" select="$parentB/*[local-name() = local-name(current())][number($posA)]"/>
      <xsl:copy>
         <xsl:choose>
            <xsl:when test="$nodeB and not($nodeB/*)">
               <xsl:copy-of select="$nodeB/node()"/>
            </xsl:when>
            <xsl:otherwise>
               <xsl:apply-templates select="@*|node()">
                  <xsl:with-param name="parentB" select="$nodeB"/>
               </xsl:apply-templates>
            </xsl:otherwise>
         </xsl:choose>
      </xsl:copy>
   </xsl:template>

   <xsl:template match="@*|node()[not(self::*)]">
      <xsl:copy>
         <xsl:apply-templates select="@*|node()"/>
      </xsl:copy>
   </xsl:template>
</xsl:stylesheet>
Tim C
  • 70,053
  • 14
  • 74
  • 93
  • Take a look at http://stackoverflow.com/questions/3511759/where-can-i-find-a-good-tutorial-on-xslt-files. The question is closed as being off-topic, but the answer point you in the right direction. – Tim C Jun 10 '14 at 14:09