Поиск XDocument с использованием LINQ без знания пространства имен
Есть ли способ поиска 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>
...
Ответы
Ответ 1
Как указывает Адам в комментарии, XName конвертируются в строку, но для этой строки требуется пространство имен, если оно есть. То почему сравнение .Name к строке терпит неудачу или почему вы не можете передать "Person" как параметр к методу XLinq для того чтобы отфильтровать их имя.
XName состоит из префикса (пространство имен) и LocalName. Локальное имя - это то, что вы хотите запросить, если вы игнорируете пространства имен.
Спасибо Адам:)
Вы не можете поместить имя node в качестве параметра метода .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);
}
Это работает для меня...
Ответ 2
Вы можете взять пространство имен из корневого элемента:
XDocument xDocument = XDocument.Load(@"C:\temp\Packet.xml");
var ns = xDocument.Root.Name.Namespace;
Теперь вы можете легко получить все нужные элементы с помощью плюса-оператора:
root.Elements(ns + "CreditCardNumber")
Ответ 3
Думаю, я нашел то, что искал. Вы можете видеть в следующем коде, я делаю оценку Element.Name.LocalName == "CreditCardNumber"
. Это, казалось, работало в моих тестах. Я не уверен, что это лучшая практика, но я буду использовать ее.
XDocument xDocument = XDocument.Load(@"C:\temp\Packet.xml");
var elements = xDocument.Root.DescendantsAndSelf().Elements().Where(d => d.Name.LocalName == "CreditCardNumber");
Теперь у меня есть элементы, где я могу зашифровать значения.
Если у кого-то есть лучшее решение, предоставьте его. Спасибо.
Ответ 4
Если ваши XML-документы всегда определяют пространство имен в том же node (Request
node в двух приведенных примерах), вы можете определить его, выполнив запрос и посмотрев, какое пространство имен получило результат:
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;
}
Ответ 5
Там пара ответов с методами расширения, которые были удалены. Не уверен почему. Здесь моя версия, которая работает для моих нужд.
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"
Там могут быть дополнительные тонкости - поэтому используйте с осторожностью.
Ответ 6
Просто используйте метод Descendents:
XDocument doc = XDocument.Load(filename);
String[] creditCards = (from creditCardNode in doc.Root.Descendents("CreditCardNumber")
select creditCardNode.Value).ToArray<string>();