tags:

views:

721

answers:

3

Is there a way to search a 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");

But what I really want, is 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")

But of course this will not work be cause I do no have a namespace.

Here's an example of the xml:

<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/"&gt;
<s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"&gt;
    <Request xmlns="http://CompanyName.AppName.Service.ContractA"&gt;
        <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/"&gt;
<s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"&gt;
    <Request xmlns="http://CompanyName.AppName.Service.ContractsB"&gt;
        <Transaction>
            <CreditCardNumber>83838</CreditCardNumber>
            <TransactionID>64588</FirstName>
        </Transaction>      
        ...
A: 

Just use the Descendents method:

XDocument doc = XDocument.Load(filename);
String[] creditCards = (from creditCardNode in doc.Root.Descendents("CreditCardNumber")
                        select creditCardNode.Value).ToArray<string>();
somerockstar
that won't work since the descendant parameter asks for an XName, and the XName is prefixed by a namespace here.
Stephane
+3  A: 

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/""&gt;
<s:Body xmlns:xsi=""http://www.w3.org/2001/XMLSchema-instance"" xmlns:xsd=""http://www.w3.org/2001/XMLSchema""&gt;
  <Request xmlns=""http://CompanyName.AppName.Service.ContractA""&gt;
    <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...

Stephane
Might help to put some explanation of *why* your answer is the way it is: Name is an XName, and XName just happens to be convertible to a string, so the comparison of .Name to a string fails with the question asker's query. XName consists of a prefix and a local name, and the local name is what you want to query on if you are ignoring namespaces.
Adam Sills
that was in the comment that I have put in somerockstar answer. I can add this for clarity, you're right
Stephane
Thanks alot for the quick help. Hopefully this will help someone else.
BarDev
Hope so, I got stuck on the same problem the first time using XLinq :)
Stephane
A: 

I thinks I found what I was looking for. You can see in the following code I do the evaluation Element.Name.LocalName == "CreditCardNumber". This seemed to work in my tests. I'm not sure if it's a best practice, but I'm going to use it.

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

Now I have elements where I can encrypt the values.

If anyone has a better solution, please provide it.

Thanks

BarDev

BarDev