Czy istnieje sposób na przeszukiwanie XDocument bez znajomości przestrzeni nazw? Mam proces, który rejestruje wszystkie żądania SOAP i szyfruje poufne dane. Chcę znaleźć wszystkie elementy na podstawie nazwy. Coś w stylu, podaj mi wszystkie elementy, na których nazwa to CreditCard. Nie obchodzi mnie, czym jest przestrzeń nazw.
Wydaje się, że mój problem dotyczy LINQ i wymaga przestrzeni nazw xml.
Mam inne procesy, które pobierają wartości z XML, ale znam przestrzeń nazw dla tych innych procesów.
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");
Naprawdę chcę mieć możliwość wyszukiwania xml bez wiedzy o przestrzeniach nazw, coś takiego:
XDocument xDocument = XDocument.Load(@"C:\temp\Packet.xml");
var elements = xDocument.Root
.DescendantsAndSelf()
.Elements()
.Where(d => d.Name == "CreditCardNumber")
To nie zadziała, ponieważ nie znam wcześniej przestrzeni nazw w czasie kompilacji.
Jak można to zrobić?
<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>
...
c#
linq-to-xml
Mike Barlow - BarDev
źródło
źródło
Odpowiedzi:
Jak Adam precyzuje w komentarzu, XName można zamienić na ciąg, ale ten ciąg wymaga przestrzeni nazw, jeśli taka istnieje. Dlatego porównanie .Name z łańcuchem nie powiedzie się lub dlaczego nie można przekazać „Person” jako parametru do metody XLinq, aby przefiltrować ich nazwę.
XName składa się z prefiksu (przestrzeni nazw) i nazwy lokalnej. Nazwa lokalna jest tym, o co chcesz zapytać, jeśli ignorujesz przestrzenie nazw.
Dziękuję Adam :)
Nie możesz umieścić nazwy węzła jako parametru metody .Descendants (), ale możesz zapytać w ten sposób:
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>");
EDYCJA: zła kopia / przeszłość z mojego testu :)
var persons = from p in doc.Descendants() where p.Name.LocalName == "Person" select p; foreach (var p in persons) { Console.WriteLine(p); }
To działa dla mnie ...
źródło
Możesz wziąć przestrzeń nazw z elementu głównego:
XDocument xDocument = XDocument.Load(@"C:\temp\Packet.xml"); var ns = xDocument.Root.Name.Namespace;
Teraz możesz łatwo uzyskać wszystkie pożądane elementy za pomocą operatora plus:
root.Elements(ns + "CreditCardNumber")
źródło
LINQ
operacji.Myślę, że znalazłem to, czego szukałem. Możesz zobaczyć w poniższym kodzie, że wykonuję ocenę
Element.Name.LocalName == "CreditCardNumber"
. To wydawało się działać w moich testach. Nie jestem pewien, czy to najlepsza praktyka, ale zamierzam z niej skorzystać.XDocument xDocument = XDocument.Load(@"C:\temp\Packet.xml"); var elements = xDocument.Root.DescendantsAndSelf().Elements().Where(d => d.Name.LocalName == "CreditCardNumber");
Teraz mam elementy, w których mogę zaszyfrować wartości.
Jeśli ktoś ma lepsze rozwiązanie, podaj je. Dzięki.
źródło
Jeśli twoje dokumenty XML zawsze definiują przestrzeń nazw w tym samym węźle (
Request
węźle w dwóch podanych przykładach), możesz to określić, wykonując zapytanie i sprawdzając, jaką przestrzeń nazw ma wynik: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; }
źródło
Istnieje kilka odpowiedzi z metodami rozszerzającymi, które zostały usunięte. Nie pewny dlaczego. Oto moja wersja, która działa na moje potrzeby.
public static class XElementExtensions { public static XElement ElementByLocalName(this XElement element, string localName) { return element.Descendants().FirstOrDefault(e => e.Name.LocalName == localName && !e.IsEmpty); } }
Służy
IsEmpty
do filtrowania węzłów za pomocąx:nil="true"
Mogą występować dodatkowe subtelności - więc używaj ich ostrożnie.
źródło
Po prostu użyj metody Descendents:
XDocument doc = XDocument.Load(filename); String[] creditCards = (from creditCardNode in doc.Root.Descendents("CreditCardNumber") select creditCardNode.Value).ToArray<string>();
źródło