Search XDocument using LINQ without knowing the namespace

后端 未结 6 1805
春和景丽
春和景丽 2020-11-30 04:49

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 elemen

相关标签:
6条回答
  • 2020-11-30 04:53

    There's a couple answers with extension methods that have been deleted. Not sure why. Here's 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);
        }
    }
    

    The IsEmpty is to filter out nodes with x:nil="true"

    There may be additional subtleties - so use with caution.

    0 讨论(0)
  • 2020-11-30 04:56

    Just use the Descendents method:

    XDocument doc = XDocument.Load(filename);
    String[] creditCards = (from creditCardNode in doc.Root.Descendents("CreditCardNumber")
                            select creditCardNode.Value).ToArray<string>();
    
    0 讨论(0)
  • 2020-11-30 05:00

    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")
    
    0 讨论(0)
  • 2020-11-30 05:00

    I think 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.

    0 讨论(0)
  • 2020-11-30 05:09

    If your XML documents always defines the namespace in the same node (Request node in the two examples given), you can determine it by making a query and seeing what namespace the result has:

    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;
    }
    
    0 讨论(0)
  • 2020-11-30 05:13

    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...

    0 讨论(0)
提交回复
热议问题