Home > database >  XSLT to add key to child nodes from parent element
XSLT to add key to child nodes from parent element

Time:11-18

I'm importing XML to an Access database. During the conversion, all relationships between data are lost. Using XSLT, I'm perpetuating the ID down through child nodes. using this, where Id is the parent element being added to child nodes:

  <xsl:template match="*[*[not(*)] and ancestor::*[Id]]">
    <xsl:copy>
      <xsl:apply-templates select="ancestor::*[Id]/Id | *"/>
    </xsl:copy>
  </xsl:template>

The problem I'm running into using this approach is there are other elements outside of the parent node that also have the Id element or variations of it, e.g. AccountId or something similar. When I attempt to use this in addition to the code above, some instances of Id go missing in previous nodes.

   <xsl:template match="*[*[not(*)] and ancestor::*[AccountId]]">
    <xsl:copy>
      <xsl:apply-templates select="ancestor::*[AccountId]/AccountId | *"/>
    </xsl:copy>
  </xsl:template>

I apologize as I don't know the appropriate language to use here as I don't know XSLT/XML very well...but what I I'm trying to accomplish is to narrow the focus of the template application to the node just below the document node (the base element node?) so that I can apply the template at a more granular level. Or find some way of perpetuating IDs when there are multiple instances of it that aren't necessarily related (e.g. example below with Link, Alice and Balance node). Notice in the first output, the Id is being perpetuated down to child nodes.

Here's an example.

XML:

<Response>


<Alices>
    <Alice>
        <Id>12345</Id>
        <Bobbers>
            <Name>John Doe</Name>
            <Bobs>
                <Bob>
                    <Organization>
                        <Name>John Doe</Name>
                        <ABB>987654</ABB>
                        <ContactDetails>
                            <Adds>
                                <Add>
                                    <Type>Postal</Type>
                                    <Line1>PO BOX 12345</Line1>
                                    <Suburb>Doeville</Suburb>
                                    <State>ENE</State>
                                    <PostCode>1111</PostCode>
                                    <Country>GB</Country>
                                    <Preferred>false</Preferred>
                                </Add>
                                <Add>
                                    <Type>Street</Type>
                                    <Line1>123 Anywhere</Line1>
                                    <Suburb>Doeville</Suburb>
                                    <State>ENE</State>
                                    <PostCode>1111</PostCode>
                                    <Country>GB</Country>
                                    <Preferred>true</Preferred>
                                </Add>
                            </Adds>
                            <PNs>
                                <PN>
                                    <Type>Mobile</Type>
                                    <Number>11111111</Number>
                                    <Preferred>true</Preferred>
                                </PN>
                            </PNs>
                            <EMs>
                                <EM>
                                    <Type>Personal</Type>
                                    <Add>[email protected]</Add>
                                    <Preferred>false</Preferred>
                                </EM>
                            </EMs>
                            <PreferredContactMethod>Email</PreferredContactMethod>
                        </ContactDetails>
                        <Contacts>
                            <Contact>
                                <LastName>Doe</LastName>
                                <FirstName>John</FirstName>
                            </Contact>
                        </Contacts>
                    </Organization>
                </Bob>
            </Bobs>
        </Bobbers>
        <Jons>
            <Jon>
                <Id>012991</Id>
                <PrimaryJon>true</PrimaryJon>
                <StartDate>1900-01-01</StartDate>
            </Jon>
        </Jons>
    </Alice>
        <Movements>
            <Movement>
                <AccountId>J54321</AccountId>
                <InvestmentCode>ABI</InvestmentCode>
                <Exchange>BRU</Exchange>
                <Id>YabbaDabba</Id>
                <Links>
                    <Link>
                        <Id>YabbaDabbaDoo</Id>
                        <Type>Stone</Type>
                    </Link>
                </Links>
            </Movement>
        </Movements>
        <Balances>
            <Balance>
                <AccountId>J54321</AccountId>
                <Value>123456</Value>
                <Investments>
                    <Investment>
                        <Code>JJJ</Code>
                        <UnitBalance>
                            <Total>112.000000</Total>
                        </UnitBalance>
                    </Investment>
                </Investments>
            </Balance>
        </Balances>
</Alices>

</Response>

Using this XSLT, without the AcctId template. Note I'm changing an element name in the Link node from Id to LinkId and it's parent element Id to TransactionId to distinguish the two.

<xsl:stylesheet
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    version="1.0">

  <xsl:output method="xml" indent="yes"/>
  <xsl:strip-space elements="*"/>

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

 
    <xsl:template match="Response/Alices/Movements/Movement/Id">
        <TransactionID>
            <xsl:apply-templates select="@* | node()"/>
        </TransactionID>
    </xsl:template>
    <xsl:template match="@TransactionID">
        <xsl:attribute name="TransactionID">
            <xsl:value-of select="."/>
        </xsl:attribute>
    </xsl:template>
    
      <xsl:template match="Response/Alices/Movements/Movement/Links/Link/Id">
        <LinkedTransactionID>
            <xsl:apply-templates select="@* | node()"/>
        </LinkedTransactionID>
    </xsl:template>
    <xsl:template match="@LinkedTransactionID">
        <xsl:attribute name="LinkedTransactionID">
            <xsl:value-of select="."/>
        </xsl:attribute>
    </xsl:template>


  <xsl:template match="*[*[not(*)] and ancestor::*[Id]]">
    <xsl:copy>
      <xsl:apply-templates select="ancestor::*[Id]/Id | *"/>
    </xsl:copy>
  </xsl:template>
 
 
  </xsl:stylesheet>

Yields this. Id elements are perpetuated from the Alice node down to its children. Also in link node, the TransactionId is also being pushed down from its parent. XSLT changes the name of this TransactionId from Id to TransactionId.

<Response>
   <Alices>
      <Alice>
         <Id>12345</Id>
         <Bobbers>
            <Id>12345</Id>
            <Name>John Doe</Name>
            <Bobs>
               <Bob>
                  <Organization>
                     <Id>12345</Id>
                     <Name>John Doe</Name>
                     <ABB>987654</ABB>
                     <ContactDetails>
                        <Id>12345</Id>
                        <Adds>
                           <Add>
                              <Id>12345</Id>
                              <Type>Postal</Type>
                              <Line1>PO BOX 12345</Line1>
                              <Suburb>Doeville</Suburb>
                              <State>ENE</State>
                              <PostCode>1111</PostCode>
                              <Country>GB</Country>
                              <Preferred>false</Preferred>
                           </Add>
                           <Add>
                              <Id>12345</Id>
                              <Type>Street</Type>
                              <Line1>123 Anywhere</Line1>
                              <Suburb>Doeville</Suburb>
                              <State>ENE</State>
                              <PostCode>1111</PostCode>
                              <Country>GB</Country>
                              <Preferred>true</Preferred>
                           </Add>
                        </Adds>
                        <PNs>
                           <PN>
                              <Id>12345</Id>
                              <Type>Mobile</Type>
                              <Number>11111111</Number>
                              <Preferred>true</Preferred>
                           </PN>
                        </PNs>
                        <EMs>
                           <EM>
                              <Id>12345</Id>
                              <Type>Personal</Type>
                              <Add>[email protected]</Add>
                              <Preferred>false</Preferred>
                           </EM>
                        </EMs>
                        <PreferredContactMethod>Email</PreferredContactMethod>
                     </ContactDetails>
                     <Contacts>
                        <Contact>
                           <Id>12345</Id>
                           <LastName>Doe</LastName>
                           <FirstName>John</FirstName>
                        </Contact>
                     </Contacts>
                  </Organization>
               </Bob>
            </Bobs>
         </Bobbers>
         <Jons>
            <Jon>
               <Id>12345</Id>
               <Id>012991</Id>
               <PrimaryJon>true</PrimaryJon>
               <StartDate>1900-01-01</StartDate>
            </Jon>
         </Jons>
      </Alice>
      <Movements>
         <Movement>
            <AccountId>J54321</AccountId>
            <InvestmentCode>ABI</InvestmentCode>
            <Exchange>BRU</Exchange>
            <TransactionID>YabbaDabba</TransactionID>
            <Links>
               <Link>
                  <TransactionID>YabbaDabba</TransactionID>
                  <LinkedTransactionID>YabbaDabbaDoo</LinkedTransactionID>
                  <Type>Stone</Type>
               </Link>
            </Links>
         </Movement>
      </Movements>
      <Balances>
         <Balance>
            <AccountId>J54321</AccountId>
            <Value>123456</Value>
            <Investments>
               <Investment>
                  <Code>JJJ</Code>
                  <UnitBalance>
                     <Total>112.000000</Total>
                  </UnitBalance>
               </Investment>
            </Investments>
         </Balance>
      </Balances>
   </Alices>
</Response>

Emphasis on this section, which in particular is what I wanted to see:

<Link>
   <TransactionID>YabbaDabba</TransactionID>
   <LinkedTransactionID>YabbaDabbaDoo</LinkedTransactionID>
   <Type>Stone</Type>
</Link>

However if I add this to the XSLT in order to perpetuate the AccountId in the Balance node to the Investment node, the Link node ID is changed. Relevant section is below the full output.

    <xsl:template match="*[*[not(*)] and ancestor::*[AccountId]]">
    <xsl:copy>
      <xsl:apply-templates select="ancestor::*[AccountId]/AccountId | *"/>
    </xsl:copy>
  </xsl:template>

Yields this, where the link node loses its parent's ID:

<Response>
   <Alices>
      <Alice>
         <Id>12345</Id>
         <Bobbers>
            <Id>12345</Id>
            <Name>John Doe</Name>
            <Bobs>
               <Bob>
                  <Organization>
                     <Id>12345</Id>
                     <Name>John Doe</Name>
                     <ABB>987654</ABB>
                     <ContactDetails>
                        <Id>12345</Id>
                        <Adds>
                           <Add>
                              <Id>12345</Id>
                              <Type>Postal</Type>
                              <Line1>PO BOX 12345</Line1>
                              <Suburb>Doeville</Suburb>
                              <State>ENE</State>
                              <PostCode>1111</PostCode>
                              <Country>GB</Country>
                              <Preferred>false</Preferred>
                           </Add>
                           <Add>
                              <Id>12345</Id>
                              <Type>Street</Type>
                              <Line1>123 Anywhere</Line1>
                              <Suburb>Doeville</Suburb>
                              <State>ENE</State>
                              <PostCode>1111</PostCode>
                              <Country>GB</Country>
                              <Preferred>true</Preferred>
                           </Add>
                        </Adds>
                        <PNs>
                           <PN>
                              <Id>12345</Id>
                              <Type>Mobile</Type>
                              <Number>11111111</Number>
                              <Preferred>true</Preferred>
                           </PN>
                        </PNs>
                        <EMs>
                           <EM>
                              <Id>12345</Id>
                              <Type>Personal</Type>
                              <Add>[email protected]</Add>
                              <Preferred>false</Preferred>
                           </EM>
                        </EMs>
                        <PreferredContactMethod>Email</PreferredContactMethod>
                     </ContactDetails>
                     <Contacts>
                        <Contact>
                           <Id>12345</Id>
                           <LastName>Doe</LastName>
                           <FirstName>John</FirstName>
                        </Contact>
                     </Contacts>
                  </Organization>
               </Bob>
            </Bobs>
         </Bobbers>
         <Jons>
            <Jon>
               <Id>12345</Id>
               <Id>012991</Id>
               <PrimaryJon>true</PrimaryJon>
               <StartDate>1900-01-01</StartDate>
            </Jon>
         </Jons>
      </Alice>
      <Movements>
         <Movement>
            <AccountId>J54321</AccountId>
            <InvestmentCode>ABI</InvestmentCode>
            <Exchange>BRU</Exchange>
            <TransactionID>YabbaDabba</TransactionID>
            <Links>
               <Link>
                  <AccountId>J54321</AccountId>
                  <LinkedTransactionID>YabbaDabbaDoo</LinkedTransactionID>
                  <Type>Stone</Type>
               </Link>
            </Links>
         </Movement>
      </Movements>
      <Balances>
         <Balance>
            <AccountId>J54321</AccountId>
            <Value>123456</Value>
            <Investments>
               <Investment>
                  <AccountId>J54321</AccountId>
                  <Code>JJJ</Code>
                  <UnitBalance>
                     <AccountId>J54321</AccountId>
                     <Total>112.000000</Total>
                  </UnitBalance>
               </Investment>
            </Investments>
         </Balance>
      </Balances>
   </Alices>
</Response>

Emphasis on these nodes:

<Link>
  <AccountId>J54321</AccountId>
  <LinkedTransactionID>YabbaDabbaDoo</LinkedTransactionID>
  <Type>Stone</Type>
</Link>

and this section is now showing the parent's ID, as I wanted.

<Investment>
    <AccountId>J54321</AccountId>
    <Code>JJJ</Code>
    <UnitBalance>
      <AccountId>J54321</AccountId>
      <Total>112.000000</Total>
    </UnitBalance>
</Investment>

I believe in this situation the issue is that Movement node also has AccountId and some child nodes have Id elements, both affected by the XSLT. So if I need to perpetuate IDs to child nodes from the parent nodes when there are multiple IDs that don't necessarily relate, how do I write the XSLT so that there is no conflict between the templates I'm trying to apply to the same node?

CodePudding user response:

Perhaps restrict the application of the second template with e.g.

<xsl:template match="*[*[not(*)] and not(Id) and ancestor::*[AccountId]]">

to only be used if the element doesn't have an Id child, like those Link elements have.

  • Related