在不知道命名空间的情况下使用 LINQ 搜索 XDocument

有没有办法在不知道名称空间的情况下搜索 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>
...
63588 次浏览

只需使用“后裔”方法:

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

正如 Adam 在注释中精确指出的那样,XName 可以转换为字符串,但是如果有字符串,则该字符串需要名称空间。这就是为什么比较。将名称传递给字符串失败,或者为什么不能将“ Person”作为参数传递给 XLinq 方法以根据它们的名称进行筛选。
XName 由前缀(Namespace)和 LocalName 组成。如果忽略名称空间,则要查询本地名称。
谢谢 Adam:)

不能将节点的 Name 作为。方法,但是可以通过这种方式进行查询:

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

编辑: < em > 测试中的错误副本/过去:)

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


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

这对我有用。

我想我找到我要找的东西了。你可以在下面的代码中看到我做了 Element.Name.LocalName == "CreditCardNumber"的计算。这在我的测试中似乎起作用了。我不确定这是不是最好的练习,但我会好好利用的。

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

现在我有了可以加密值的元素。

如果有人有更好的解决方案,请提供。谢谢。

如果 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;
}

您可以从根元素获取名称空间:

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

现在你可以使用加运算符很容易地得到所有你想要的元素:

root.Elements(ns + "CreditCardNumber")

有一对夫妇的答案与扩展方法已被删除。不知道为什么。这是我的版本,适合我的需要。

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"过滤掉节点

可能还有其他的微妙之处——所以要小心使用。

我有一个很大的问题: “我知道这是解决方案,但我对 那个是解决方案感到失望”... ... 我最近写了一个类似下面这样的查询(我很快会替换它,但它具有教育价值) :

var result = xdoc.Descendants("{urn:schemas-microsoft-com:rowset}data")
.FirstOrDefault()?
.Descendants("{#RowsetSchema}row");

如果我从 XML 中删除名称空间,我可以像下面这样编写相同的查询:

var result = xdoc.Descendants("data")
.FirstOrDefault()?
.Descendants("row");

我计划编写自己的扩展方法,这些方法应该允许我不使用名称空间,而是像下面这样搜索节点:

var result = xdoc.Descendants("rs:data")
.FirstOrDefault()?
.Descendants("z:row");
//'rs:' {refers to urn:schemas-microsoft-com:rowset}
//'z:' {refers to xmlns:z=#RowsetSchema}

我在代码下面的注释指出了我希望如何在扩展方法库中隐藏解决方案的丑陋。同样,我知道前面提到的解决方案-但是我希望 API 本身能够更多地处理这个 很流利。(看到我做了什么吗?)