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.