I've got a feeling that this question is quite straightforward but it is years since I did any xslt so maybe someone can help?
I have a piece of xml which has been generated by the .net class DataContractSerializer, and I need to extract data from this xml using xslt to end up with some html. The thing that is complicating things for me is the heavy use of namespaces...
A snippet of the xml looks like this:
<FundDeal xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://schemas.datacontract.org/2004/07/Guide.Rx.BusinessObjects.Deal">
<Id xmlns="http://schemas.datacontract.org/2004/07/Guide.BusinessObjects.Deal">DEAL12345</Id>
<Account xmlns:d2p1="http://schemas.datacontract.org/2004/07/Guide.Rx.BusinessObjects.Account">
<d2p1:AlternateId i:nil="true"/>
<d2p1:Designation>XXX</d2p1:Designation>
<d2p1:Name>QWERTY</d2p1:Name>
<d2p1:Number>12345678</d2p1:Number>
<d2p1:Status i:nil="true"/>
</Account>
<Agent xmlns:d2p1="http://schemas.datacontract.org/2004/07/Guide.Rx.BusinessObjects.Account">
<d2p1:Id>54321</d2p1:Id>
<d2p1:Name>ASDFG</d2p1:Name>
<d2p1:Status>Active</d2p1:Status>
</Agent>
....
</FundDeal>
Now, I need to on transform this xml through a stylesheet and am finding the going quite tough. I recognise that the xsl needs its own reference to the namespaces involved, and can extract things like the Deal Id above easily with the following xsl:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:ms="urn:schemas-microsoft-com:xslt"
xmlns:grbd="http://schemas.datacontract.org/2004/07/Guide.Rx.BusinessObjects.Deal"
xmlns:gbd="http://schemas.datacontract.org/2004/07/Guide.BusinessObjects.Deal"
xmlns:grba="http://schemas.datacontract.org/2004/07/Guide.Rx.BusinessObjects.Account">
<xsl:output indent="yes" omit-xml-declaration="yes" method="html"/>
<xsl:template match="/">
<html>
<head>
<!-- some styles here -->
</head>
<body>
<table cellpadding="5" cellspacing="5" border="0">
<tr>
<td class="SectionTitle" colspan="2">
<xsl:text>Deal Cancellation Notification - </xsl:text>
<xsl:value-of select="//ggbd:Id"/>
</td>
</tr>
</table>
</body>
</html>
</xsl:template>
</xsl:stylesheet>
but I'm struggling to read things like the Account Name because there appear to be multiple namespaces going on.
Can anyone tell me the xpath to access (a) Account Name, and (b) Agent Name? I think seeing how to access these will probably allow me to access everything else I need.
Many thanks, Pete
If you are going to work with XML, it is worth getting your head around Namespaces - painful as that might be. Postponing your understanding will only make things even more painful in the long run.
There are not "multiple namespaces" for the Account Name or Agent Name: an element is only ever in at most one Namespace.
Most of the Namespace syntax you are seeing is merely binding namespace prefixes to Namespace names (URIs). So when you see
xmlns:i="http://www.w3.org/2001/XMLSchema-instance"
this is binding the prefix "i" to the URI "http://www.w3.org/2001/XMLSchema-instance", so that deeper elements in the document might use the "i" prefix (essentially as a way of saving keystrokes).
When the xmlns attribute is specified on its own with a value (i.e. you see xmlns="something"), this means that Namespace is an effect for this element and its descendants (unless overridden by a another Namespace being specified at a deeper level).
Thus, in your example document (which is a bit of a Namespace hodge-podge) the Namespace Name of the root FundDeal element is "http://schemas.datacontract.org/2004/07/Guide.Rx.BusinessObjects.Deal", and this is also the case for its child Account and Agent elements (although they happen to define a namespace/prefix binding this does not affect their own Namespace: this binding is used by their child elements).
You can specify the Namespaces in your stylesheet most easily by binding you own prefixes ("fund" and "deal" in the example below) to refer to Namespaces you need (I have added a bit more which I hope makes it a bit clearer):
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:fund="http://schemas.datacontract.org/2004/07/Guide.Rx.BusinessObjects.Deal"
xmlns:deal="http://schemas.datacontract.org/2004/07/Guide.BusinessObjects.Deal"
xmlns:d2p1="http://schemas.datacontract.org/2004/07/Guide.Rx.BusinessObjects.Account">
<xsl:output indent="yes" omit-xml-declaration="yes" method="html"/>
<xsl:template match="/">
<html>
<head>
<!-- some styles here -->
</head>
<body>
<table cellpadding="5" cellspacing="5" border="0">
<tr>
<td class="SectionTitle" colspan="2">
<xsl:text>Deal Cancellation Notification - </xsl:text>
<xsl:value-of select="/fund:FundDeal/deal:Id"/>
<br/>
<xsl:text>Account Name - </xsl:text>
<xsl:value-of select="/fund:FundDeal/fund:Account/d2p1:Name"/>
<br/>
<xsl:text>Agent Name - </xsl:text>
<xsl:value-of select="/fund:FundDeal/fund:Agent/d2p1:Name"/>
</td>
</tr>
</table>
</body>
</html>
</xsl:template>
</xsl:stylesheet>
This is going to work, but it's not the proper way:
//*[local-name()='Account']/grba:Name
and
//*[local-name()='Agent']/grba:Name
Looking better at your input, you have parent namespaces. You need to select the correct namespace of the element. For example Account and Agent are in grbd scope, while Name is in grba. Following your namespace declarations you can select as follows:
//grbd:Account/grba:Name
or
//grbd:Agent/grba:Name
This is true for elements with prefix namespace. Otherwise you should select the local one. For example for the first Id node, you need:
//gbd:Id
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With