Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Search XDocument using LINQ without knowing the namespace

Tags:

c#

linq-to-xml

Is there a way to search an XDocument without knowing the namespace? I have a process that logs all SOAP requests and encrypts the sensitive data. I want to find any elements based on name. Something like, give me all elements where the name is CreditCard. I don't care what the namespace is.

My problem seems to be with LINQ and requiring a xml namespace.

I have other processes that retrieve values from XML, but I know the namespace for these other process.

XDocument xDocument = XDocument.Load(@"C:\temp\Packet.xml"); XNamespace xNamespace = "http://CompanyName.AppName.Service.Contracts";  var elements = xDocument.Root                         .DescendantsAndSelf()                         .Elements()                         .Where(d => d.Name == xNamespace + "CreditCardNumber"); 

I really want to have the ability to search xml without knowing about namespaces, something like this:

XDocument xDocument = XDocument.Load(@"C:\temp\Packet.xml"); var elements = xDocument.Root                         .DescendantsAndSelf()                         .Elements()                         .Where(d => d.Name == "CreditCardNumber") 

This will not work because I don't know the namespace beforehand at compile time.

How can this be done?

<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/"> <s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">     <Request xmlns="http://CompanyName.AppName.Service.ContractA">         <Person>             <CreditCardNumber>83838</CreditCardNumber>             <FirstName>Tom</FirstName>             <LastName>Jackson</LastName>         </Person>         <Person>             <CreditCardNumber>789875</CreditCardNumber>             <FirstName>Chris</FirstName>             <LastName>Smith</LastName>         </Person>         ...  <s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/"> <s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">     <Request xmlns="http://CompanyName.AppName.Service.ContractsB">         <Transaction>             <CreditCardNumber>83838</CreditCardNumber>             <TransactionID>64588</FirstName>         </Transaction>               ... 
like image 643
Mike Barlow - BarDev Avatar asked Apr 09 '10 21:04

Mike Barlow - BarDev


2 Answers

As Adam precises in the comment, XName are convertible to a string, but that string requires the namespace when there is one. That's why the comparison of .Name to a string fails, or why you can't pass "Person" as a parameter to the XLinq Method to filter on their name.
XName consists of a prefix (the Namespace) and a LocalName. The local name is what you want to query on if you are ignoring namespaces.
Thank you Adam :)

You can't put the Name of the node as a parameter of the .Descendants() method, but you can query that way :

var doc= XElement.Parse( @"<s:Envelope xmlns:s=""http://schemas.xmlsoap.org/soap/envelope/""> <s:Body xmlns:xsi=""http://www.w3.org/2001/XMLSchema-instance"" xmlns:xsd=""http://www.w3.org/2001/XMLSchema"">   <Request xmlns=""http://CompanyName.AppName.Service.ContractA"">     <Person>         <CreditCardNumber>83838</CreditCardNumber>         <FirstName>Tom</FirstName>         <LastName>Jackson</LastName>     </Person>     <Person>         <CreditCardNumber>789875</CreditCardNumber>         <FirstName>Chris</FirstName>         <LastName>Smith</LastName>     </Person>    </Request>    </s:Body> </s:Envelope>"); 

EDIT : bad copy/past from my test :)

var persons = from p in doc.Descendants()               where p.Name.LocalName == "Person"               select p;  foreach (var p in persons) {     Console.WriteLine(p); } 

That works for me...

like image 51
Stéphane Avatar answered Sep 29 '22 10:09

Stéphane


You could take the namespace from the root-element:

XDocument xDocument = XDocument.Load(@"C:\temp\Packet.xml"); var ns = xDocument.Root.Name.Namespace; 

Now you can get all desired elements easily using the plus-operator:

root.Elements(ns + "CreditCardNumber") 
like image 43
Sascha Avatar answered Sep 29 '22 09:09

Sascha