Поиск XDocument с использованием LINQ, не зная пространства имен - PullRequest
72 голосов
/ 10 апреля 2010

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

Кажется, моя проблема связана с 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>      
        ...

Ответы [ 6 ]

82 голосов
/ 10 апреля 2010

Как уточнил Адам в комментарии, XName можно преобразовать в строку, но эта строка требует пространства имен, если оно есть. Вот почему сравнение .Name со строкой не удается или почему вы не можете передать «Person» в качестве параметра методу XLinq для фильтрации по их имени.
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>");

РЕДАКТИРОВАТЬ: плохая копия / прошлое моего теста:)

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

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

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

81 голосов
/ 08 октября 2011

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

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

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

root.Elements(ns + "CreditCardNumber")
12 голосов
/ 10 апреля 2010

Я думаю, что нашел то, что искал. Вы можете увидеть в следующем коде я делаю оценку 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 голосов
/ 17 августа 2011

Если ваши 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;
}
0 голосов
/ 16 мая 2019

Есть пара ответов с методами расширения, которые были удалены. Не уверен почему. Вот моя версия, которая работает для моих нужд.

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 - отфильтровывать узлы с x:nil="true"

Там могут быть дополнительные тонкости - поэтому используйте с осторожностью.

0 голосов
/ 10 апреля 2010

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

XDocument doc = XDocument.Load(filename);
String[] creditCards = (from creditCardNode in doc.Root.Descendents("CreditCardNumber")
                        select creditCardNode.Value).ToArray<string>();
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...