2010-04-09 2 views
62

Есть ли способ поиска XDocument без знания пространства имен? У меня есть процесс, который регистрирует все запросы SOAP и шифрует конфиденциальные данные. Я хочу найти любые элементы, основанные на имени. Что-то вроде, дайте мне все элементы, где имя - CreditCard. Меня не волнует, что такое пространство имен.Поиск XDocument с использованием LINQ без знания пространства имен

Моя проблема связана с LINQ и требует пространства имен xml.

У меня есть другие процессы, которые извлекают значения из XML, но я знаю пространство имен для этого другого процесса.

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

Я действительно хочу, чтобы иметь возможность сделать поиск XML, не зная о пространствах имен, что-то вроде этого:

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

Это не будет работать, потому что я не знаю имен заранее на время компиляции.

Как это можно сделать?

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

Отметьте этот ответ из другого вопроса: http://stackoverflow.com/questions/934486/how-do-i-get-a-nametable-from-an-xdocument/3977823#3977823 – MonkeyWrench

ответ

71

Как Адам precises в комментариях, XName может быть конвертирован в строку, но эта строка требует имен, когда есть один. Вот почему сравнение .Name с строкой терпит неудачу или почему вы не можете передать «Person» в качестве параметра XLinq Method для фильтрации по их имени.
XName состоит из префикса (пространство имен) и LocalName. Локальное имя - это то, что вы хотите запросить, если вы игнорируете пространства имен.
Спасибо Адам :)

Вы не можете поместить имя узла в качестве параметра из .Descendants() метод, но вы можете запросить этот путь:

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:плохая копия/прошлое из моего теста :)

var persons = from p in doc.Descendants() 
       where p.Name.LocalName == "Person" 
       select p; 

foreach (var p in persons) 
{ 
    Console.WriteLine(p); 
} 

Это работает для меня ...

+3

Может помочь пояснить * почему * ваш ответ такой, какой он есть: Имя - это XName, а XName просто конвертируется в строку, поэтому сравнение .Name с строкой не выполняется с запросом респондента вопроса , XName состоит из префикса и локального имени, а локальное имя - это то, что вы хотите запросить, если вы игнорируете пространства имен. –

+0

, который был в комментарии, который я поставил в somerockstar. Я могу добавить это для ясности, вы правы –

+0

Спасибо за помощь. Надеюсь, это поможет кому-то другому. –

-6

Просто используйте метод Descendents:

XDocument doc = XDocument.Load(filename); 
String[] creditCards = (from creditCardNode in doc.Root.Descendents("CreditCardNumber") 
         select creditCardNode.Value).ToArray<string>(); 
+2

, который не будет работать поскольку параметр descendant запрашивает имя XName, а XName имеет префикс пространства имен здесь. –

8

Думаю, я нашел то, что искал. Вы можете видеть в следующем коде, я делаю оценку Element.Name.LocalName == "CreditCardNumber". Это, казалось, работало в моих тестах. Я не уверен, что это лучшая практика, но я буду использовать ее.

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

Теперь у меня есть элементы, где я могу зашифровать значения.

Если у кого-то есть лучшее решение, предоставьте его. Благодарю.

2

Если XML-документы всегда определяет пространство имен в том же узле (Request узел в двух приведенных примерах), вы можете определить его, сделав запрос и видеть то, что пространство имен результат есть:

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

Вы может занять пространство имен из корневого элемента:

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

Теперь вы можете получить все нужные элементы легко с помощью плюс-оператора:

root.Elements(ns + "CreditCardNumber") 
+0

Кажется, это лучший ответ, поскольку он по-прежнему позволяет вам использовать большинство операций LINQ. –

+1

Этот ответ допустим только в том случае, если ни один из элементов не находится в другом пространстве имен в качестве корневого документа. Да, легко знать пространство имен, если вы просто попросите у него корневой документ, но его сложнее запросить какие-либо элементы заданного имени, независимо от того, какое пространство имен может содержать сам элемент. Вот почему я рассматриваю использование ответа XElement.Name.LocalName (обычно через linq) более обобщены. –

Смежные вопросы