user1140422
user1140422

Reputation: 21

Using XQUERY to retrieve attributes value

Is it possible to use XQUERY to retrieve the attributes filename from the following XML? I am trying to use /preFileDoc/inpXML/@filename but it doesn't work...

<?xml version="1.0"?>
<preFileDoc xmlns:soap-env="http://schemas.xmlsoap.org/soap/envelope/">
  <senderId>ABC</senderId>
  <receiverId>XYZ</receiverId>
  <tranxCode>A001</tranxCode>
  <inpXML version="1.0" encoding="UTF-8">
    <soap-env:Envelope>
      <soap-env:Header msgcode="SPPCONVAKT" orig-system="002FTB" refid="65355ff50a172064484bf9da64c1e245" timestamp="2009-02-11 21:00:10.741" filename="SPPCONVAKT20090128001.dat"/>
      <soap-env:Body>
text1
text2
      </soap-env:Body>
    </soap-env:Envelope>
  </inpXML>
</preFileDoc>

ps: Sometimes the filename attributes is sent as fileName in the incoming XML..thinking to retrieve value from attributes @filename OR @fileName.. can it achieve in single XQUERY? Thanks for advice...

Upvotes: 2

Views: 2326

Answers (3)

Leo W&#246;rteler
Leo W&#246;rteler

Reputation: 4241

I think your XPath is incomplete. The last child-step / in /preFileDoc/inpXML/@filename only matches attributes of the inpXML element, not its descendants.

One way to solve the problem would be the //-step:

/preFileDoc/inpXML//@filename

Note that this would find all attributes named filename in the soapenv:Body, too.

A more robust way would thus be to declare the soapenv prefix in the XQuery:

declare namespace soap-env="http://schemas.xmlsoap.org/soap/envelope/";

return /preFileDoc/inpXML//soap-env:Header/@filename

Finally, the different capitalizations of filename can be worked around by specifying both:

declare namespace soap-env="http://schemas.xmlsoap.org/soap/envelope/";

return /preFileDoc/inpXML//soap-env:Header/(@filename | @fileName)

Upvotes: 2

grtjn
grtjn

Reputation: 20414

You can take the union of multiple attributes. It will be unlikely that this attribute will appear multiple times with different casing, so that should always return a single node:

//soap-env:Header/@filename | //soap-env:Header/@fileName

Optionally, you could wrap it in parentheses, and add [1] behind it, to always take the first result.

(//soap-env:Header/@filename | //soap-env:Header/@fileName)[1]

If you replace the union with a comma, which creates a sequence instead of a document order node set, you can add a default as well at the end. Maybe not very usefull here, but perhaps in other situations:

(//soap-env:Header/@filename , //soap-env:Header/@fileName, "default.dat")[1]

HTH!

Upvotes: 1

marc_s
marc_s

Reputation: 754963

You need to respect and take into account the SOAP XML namespace!

Since I don't know what you're using, I cannot tell you how to do this - but there's the xmlns:soap-env="http://schemas.xmlsoap.org/soap/envelope/" on the root node, and your @filename attribute is on the <soap-env:Header .... /> node - so you need to include the XML namespace in your XQuery.

In .NET / C#, you could do it like this (using the "older" XmlDocument style which supports XPath directly):

// define test XML 
string xmlContent = 
   @"<?xml version='1.0'?>
     <preFileDoc xmlns:soap-env='http://schemas.xmlsoap.org/soap/envelope/'>
        <senderId>ABC</senderId>
        <receiverId>XYZ</receiverId>
        <tranxCode>A001</tranxCode>
        <inpXML version='1.0' encoding='UTF-8'>
           <soap-env:Envelope>
              <soap-env:Header msgcode='SPPCONVAKT' orig-system='002FTB' refid='65355ff50a172064484bf9da64c1e245' timestamp='2009-02-11 21:00:10.741' filename='SPPCONVAKT20090128001.dat'/>
              <soap-env:Body>
                text1
                text2
              </soap-env:Body>
           </soap-env:Envelope>
        </inpXML>
     </preFileDoc>";

// create XmlDocument and load test data
XmlDocument doc = new XmlDocument();
doc.LoadXml(xmlContent);

// define XML namespace manager and add the SOAP namespace to it
XmlNamespaceManager mgr = new XmlNamespaceManager(doc.NameTable);
mgr.AddNamespace("soap", "http://schemas.xmlsoap.org/soap/envelope/");

// use XPath and the XML namespaces to grab the <Header> node
// the first two nodes <preFileDoc> and <inpXML> are not inside any explicit
// XML namespace
// but the next two (<Envelope> and <Header>) are in the "soap" XML namespace
XmlNode header = doc.SelectSingleNode("/preFileDoc/inpXML/soap:Envelope/soap:Header", mgr);

// read the "filename" attribute from the header node
if(header != null && header.Attributes["filename"] != null)
{
    string fileName = header.Attributes["filename"].Value;
}

Upvotes: 0

Related Questions