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

发布于 2024-08-28 11:51:28 字数 2096 浏览 5 评论 0原文

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

Is there a way to search an XDocument without knowing the namespace? I have a process that logs all SOAP requests and encrypts the sensitive data. I want to find any elements based on name. Something like, give me all elements where the name is CreditCard. I don't care what the namespace is.

My problem seems to be with LINQ and requiring a xml namespace.

I have other processes that retrieve values from XML, but I know the namespace for these other process.

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

I really want to have the ability to search xml without knowing about namespaces, something like this:

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

This will not work because I don't know the namespace beforehand at compile time.

How can this be done?

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(7

_失温 2024-09-04 11:51:28

正如 Adam 在评论中精确指出的那样,XName 可以转换为字符串,但该字符串需要命名空间(当有命名空间时)。这就是 .Name 与字符串的比较失败的原因,或者无法将“Person”作为参数传递给 XLinq 方法以过滤其姓名的原因。

XName 由前缀(命名空间)和本地名称组成。如果您忽略命名空间,则本地名称是您想要查询的名称。
谢谢 Adam :)

您不能将节点的名称作为 .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);
}

这对我有用......

As Adam precises in the comment, XName are convertible to a string, but that string requires the namespace when there is one. That's why the comparison of .Name to a string fails, or why you can't pass "Person" as a parameter to the XLinq Method to filter on their name.
XName consists of a prefix (the Namespace) and a LocalName. The local name is what you want to query on if you are ignoring namespaces.
Thank you Adam :)

You can't put the Name of the node as a parameter of the .Descendants() method, but you can query that way :

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 : bad copy/past from my test :)

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

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

That works for me...

山色无中 2024-09-04 11:51:28

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

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

现在您可以使用加号运算符轻松获取所有所需的元素:

root.Elements(ns + "CreditCardNumber")

You could take the namespace from the root-element:

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

Now you can get all desired elements easily using the plus-operator:

root.Elements(ns + "CreditCardNumber")
晌融 2024-09-04 11:51:28

我想我找到了我正在寻找的东西。您可以在下面的代码中看到我执行了评估 Element.Name.LocalName == "CreditCardNumber"。这在我的测试中似乎有效。我不确定这是否是最佳实践,但我会使用它。

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

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

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

I think I found what I was looking for. You can see in the following code I do the evaluation Element.Name.LocalName == "CreditCardNumber". This seemed to work in my tests. I'm not sure if it's a best practice, but I'm going to use it.

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

Now I have elements where I can encrypt the values.

If anyone has a better solution, please provide it. Thanks.

雨的味道风的声音 2024-09-04 11:51:28

有几个扩展方法的答案已被删除。不知道为什么。这是适合我的需要的版本。

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" 的节点。

可能还有其他微妙之处 - 因此请谨慎使用。

There's a couple answers with extension methods that have been deleted. Not sure why. Here's my version that works for my needs.

public static class XElementExtensions
{
    public static XElement ElementByLocalName(this XElement element, string localName)
    {
        return element.Descendants().FirstOrDefault(e => e.Name.LocalName == localName && !e.IsEmpty);
    }
}

The IsEmpty is to filter out nodes with x:nil="true"

There may be additional subtleties - so use with caution.

笑脸一如从前 2024-09-04 11:51:28

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

If your XML documents always defines the namespace in the same node (Request node in the two examples given), you can determine it by making a query and seeing what namespace the result has:

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;
}
远昼 2024-09-04 11:51:28

我正遭受着“我知道这就是解决方案,但我对就是解决方案”感到失望......我最近写了一个如下所示的查询(我很快就会替换,但它具有教育价值):

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 本身能够更流畅地处理这个问题。 (看看我在那里做了什么?)

I a suffering from a major case of "I know that is the solution, but I am disappointed that that is the solution"... I recently wrote a query like the one below (which I will shortly replace, but it has educational value):

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

If I remove the namespaces from the XML, I can write the same query like this:

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

I plan to write my own extension methods that should allow me to leave the namespaces alone and search for nodes like this:

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

My comments just below the code point to how I would like to hide the ugliness of the solution in an Extension Methods library. Again, I'm aware of the solutions posted earlier - but I wish the API itself handled this more fluently. (See what I did there?)

北方的韩爷 2024-09-04 11:51:28

只需使用后代方法:

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

Just use the Descendents method:

XDocument doc = XDocument.Load(filename);
String[] creditCards = (from creditCardNode in doc.Root.Descendents("CreditCardNumber")
                        select creditCardNode.Value).ToArray<string>();
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文