XDocument search using LINQ without namespace knowledge

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

My problem seems to be related to LINQ and requires an xml namespace.

I have other processes that extract values ​​from XML, but I know the namespace for this 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 be able to search for 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 do not know the namespace in advance at compile time.

How can I do that?

 <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> ... 
+70
c # linq-to-xml
Apr 09 '10 at 21:09
source share
6 answers

As Adam points out in a comment, XName are converted to a string, but this string requires a namespace, if any. That is why the .Name comparison to the string fails or why you cannot pass "Person" as an argument to the XLinq method to filter their name.
XName consists of a prefix (namespace) and LocalName. A local name is what you want to request if you ignore namespaces.
Thanks Adam :)

You cannot put the name of the node as a parameter to the .Descendants () method, but you can query it like this:

 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); } 

This works for me ...

+81
Apr 09 '10 at 21:44
source share

You can take the namespace from the root element:

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

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

 root.Elements(ns + "CreditCardNumber") 
+80
Oct 08 2018-11-11T00:
source share

I think I found what I was looking for. You can see in the following code that I am evaluating Element.Name.LocalName == "CreditCardNumber" . This seemed to work in my tests. I am not sure if this is the best practice, but I will 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, provide one. Thank.

+11
Apr 09 2018-10-09T00:
source share

If your XML documents always define a namespace in the same node ( Request node in the two examples above), you can define it by executing the query and looking at which namespace the result is:

 XDocument xDoc = XDocument.Load("filename.xml"); //Initial query to get namespace: var reqNodes = from el in xDoc.Root.Descendants() where el.Name.LocalName == "Request" select el; foreach(var reqNode in reqNodes) { XNamespace xns = reqNode.Name.Namespace; //Queries making use of namespace: var person = from el in reqNode.Elements(xns + "Person") select el; } 
+2
Aug 17 2018-11-11T00:
source share

There are a couple of answers with extension methods that have been removed. Not sure why. Here is my version that works for my needs.

 public static class XElementExtensions { public static XElement ElementByLocalName(this XElement element, string localName) { return element.Descendants().FirstOrDefault(e => e.Name.LocalName == localName && !e.IsEmpty); } } 

IsEmpty should filter out nodes with x:nil="true"

There may be additional subtleties - so use with caution.

0
May 16 '19 at 19:43
source share

Just use the Descendents method:

 XDocument doc = XDocument.Load(filename); String[] creditCards = (from creditCardNode in doc.Root.Descendents("CreditCardNumber") select creditCardNode.Value).ToArray<string>(); 
-6
Apr 09 '10 at 21:33
source share



All Articles