Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to deal with XML namespaces

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

like image 386
PeteH Avatar asked Dec 10 '25 09:12

PeteH


2 Answers

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>
like image 190
alexbrn Avatar answered Dec 13 '25 14:12

alexbrn


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
like image 27
Emiliano Poggi Avatar answered Dec 13 '25 15:12

Emiliano Poggi



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!