2

Using XSLT, I am trying to figure out how to merge/update the data in a set of nodes with data from another set of nodes. The nodes have the same schema, but different parents. The data needs to be merged based on a shared parent attribute. In the example below, data is being copied from Principal to Driver. Can anyone help me out here?

Input File:

<Info>
  <Principal id="Insured">
    <PersonName>
      <GivenName>Jane</GivenName>
      <OtherGivenName>A</OtherGivenName>
      <Surname>Doe</Surname>
    </PersonName>
    <PersonInfo>
      <BirthDate>01-01-1980</BirthDate>
      <MaritalStatus>M</MaritalStatus>
    </PersonInfo>
    <PrincipalInfo></PrincipalInfo>
  </Principal>
  <Policy>
    <Driver id="Insured">
      <PersonName>
        <GivenName>Jane</GivenName>
        <Surname>Smith</Surname>
      </PersonName>
      <PersonInfo>
        <BirthDate>01-01-1980</BirthDate>
        <MaritalStatus>S</MaritalStatus>
        <Occupation>Manager</Occupation>
      </PersonInfo>
    </Driver>
    <PolicyInfo></PolicyInfo>
  </Policy>
</Info>

Desired Result:

<Info>
  <Principal id="Insured">
    <PersonName>
      <GivenName>Jane</GivenName>
      <OtherGivenName>A</OtherGivenName>
      <Surname>Doe</Surname>
    </PersonName>
    <PersonInfo>
      <BirthDate>01-01-1980</BirthDate>
      <MaritalStatus>M</MaritalStatus>
    </PersonInfo>
    <PrincipalInfo></PrincipalInfo>
  </Principal>
  <Policy>
    <Driver id="Insured">
      <PersonName>
        <GivenName>Jane</GivenName>
        <OtherGivenName>A</OtherGivenName>
        <Surname>Doe</Surname>
      </PersonName>
      <PersonInfo>
        <BirthDate>01-01-1980</BirthDate>
        <MaritalStatus>M</MaritalStatus>
        <Occupation>Manager</Occupation>
      </PersonInfo>
    </Driver>
    <PolicyInfo></PolicyInfo>
  </Policy>
</Info>
4
  • Is it a real deal? You basically replace PersonName with new values. No merging occurs. Commented Feb 12, 2011 at 16:42
  • There may be extra nodes in the Driver subnodes, that I want to leave, so just replacing the entire node doesn't work. Note the <Occupation> in my example. Commented Feb 12, 2011 at 17:02
  • Also, the node from <Principal> may not yet exist in the <Driver>, note the <OtherGivenName>. Commented Feb 12, 2011 at 17:12
  • Good question, +1. See my answer for a complete, short and easy solution that uses the most fundamental and powerful XSLT design pattern: the overriding of the identity rule. A thorough explanation is provided. Commented Feb 12, 2011 at 18:26

1 Answer 1

2

Here is a complete solution:

<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:key name="kPrincipalById" match="Principal"
  use="@id"/>

 <xsl:key name="kPrincipalChild" match="Principal/*/*"
  use="concat(../../@id, name())"/>

 <xsl:template match="node()|@*">
  <xsl:copy>
   <xsl:apply-templates select="node()|@*"/>
  </xsl:copy>
 </xsl:template>

 <xsl:template match="Driver/*">
  <xsl:variable name="vPrincipal"
   select="key('kPrincipalById', ../@id)"/>

   <xsl:copy>
    <xsl:apply-templates select="@*"/>
    <xsl:apply-templates select=
     "$vPrincipal/*[name()=name(current())]/*"/>
    <xsl:apply-templates select=
     "*[not(key('kPrincipalChild',
                 concat(../../@id,name())
                 )
            )
        ]"/>
   </xsl:copy>
 </xsl:template>
</xsl:stylesheet>

when this transformation is applied on the provided XML document:

<Info>
    <Principal id="Insured">
        <PersonName>
            <GivenName>Jane</GivenName>
            <OtherGivenName>A</OtherGivenName>
            <Surname>Doe</Surname>
        </PersonName>
        <PersonInfo>
            <BirthDate>01-01-1980</BirthDate>
            <MaritalStatus>M</MaritalStatus>
        </PersonInfo>
        <PrincipalInfo></PrincipalInfo>
    </Principal>
    <Policy>
        <Driver id="Insured">
            <PersonName>
                <GivenName>Jane</GivenName>
                <Surname>Smith</Surname>
            </PersonName>
            <PersonInfo>
                <BirthDate>01-01-1980</BirthDate>
                <MaritalStatus>S</MaritalStatus>
                <Occupation>Manager</Occupation>
            </PersonInfo>
        </Driver>
        <PolicyInfo></PolicyInfo>
    </Policy>
</Info>

the wanted, correct result is produced:

<Info>
   <Principal id="Insured">
      <PersonName>
         <GivenName>Jane</GivenName>
         <OtherGivenName>A</OtherGivenName>
         <Surname>Doe</Surname>
      </PersonName>
      <PersonInfo>
         <BirthDate>01-01-1980</BirthDate>
         <MaritalStatus>M</MaritalStatus>
      </PersonInfo>
      <PrincipalInfo/>
   </Principal>
   <Policy>
      <Driver id="Insured">
         <PersonName>
            <GivenName>Jane</GivenName>
            <OtherGivenName>A</OtherGivenName>
            <Surname>Doe</Surname>
         </PersonName>
         <PersonInfo>
            <BirthDate>01-01-1980</BirthDate>
            <MaritalStatus>M</MaritalStatus>
            <Occupation>Manager</Occupation>
         </PersonInfo>
      </Driver>
      <PolicyInfo/>
   </Policy>
</Info>

Explanation:

  1. The identity rule/template copies every node "as-is". The use and overriding of the identity rule is the most fundamental and powerful XSLT design pattern.

  2. There is just one additional template that overrides the identity rule for children-elements of Driver. It copies (and effectively replaces the same-named grand-child elements of Driver with the corresponding) grand-child elements of Principal. Then it still processes (copies) those grand-children elements of Driver that do not have corresponding grand-children elements of Principal

  3. For convenient access to Principal and its grand-children -- by id and id++name(), there are two keys defined and used.

Sign up to request clarification or add additional context in comments.

2 Comments

I'm new to XSLT and still don't completely understand your solution, but it appears to work for every test I've thrown at it.
@Tim-B: You are welcome. To better understand some key points in this solution, please, read more on the subject of "identity rule" and xslt keys. I have edited my answer and now the explanation has links to these two topics. To learn more about XSLT/XPath, you can use the resources identified in this answer: stackoverflow.com/questions/339930/…

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.