【问题标题】:Remove all nodes in a specified namespace from XML从 XML 中删除指定命名空间中的所有节点
【发布时间】:2015-06-23 13:33:01
【问题描述】:

我有一个 XML 文档,其中包含命名空间中的一些内容。这是一个例子:

<?xml version="1.0" encoding="UTF-8"?>
<root xmlns:test="urn:my-test-urn">
    <Item name="Item one">
        <test:AlternativeName>Another name</test:AlternativeName>
        <Price test:Currency="GBP">124.00</Price>
    </Item>
</root>

我想删除 test 命名空间中的所有内容 - 不仅从标签中删除命名空间前缀,而且实际上从文档中删除所有节点(元素和属性)(在本例中)在test 命名空间中。我需要的输出是:

<?xml version="1.0" encoding="UTF-8"?>
<root xmlns:test="urn:my-test-urn">
    <Item name="Item one">
        <Price>124.00</Price>
    </Item>
</root>

我目前并不太担心命名空间声明是否仍然存在,现在我很乐意只删除指定命名空间中的内容。请注意,文档中可能有多个命名空间要修改,因此我希望能够指定要删除哪个命名空间。

我尝试过使用 .Descendants().Where(e =&gt; e.Name.Namespace == "test") 来做这件事,但这只是为了返回一个 IEnumerable&lt;XElement&gt; 所以它不能帮助我找到属性,如果我使用 .DescendantNodes() 我看不到查询命名空间前缀,因为这似乎不是 XNode 上的属性。

我可以遍历每个元素,然后遍历元素上的每个属性,检查每个人的Name.Namespace,但这似乎不优雅且难以阅读。

有没有办法使用 LINQ to Xml 实现这一点?

【问题讨论】:

    标签: c# xml linq-to-xml


    【解决方案1】:

    遍历元素然后遍历属性似乎并不难阅读:

    var xml = @"<?xml version='1.0' encoding='UTF-8'?>
    <root xmlns:test='urn:my-test-urn'>
        <Item name='Item one'>
            <test:AlternativeName>Another name</test:AlternativeName>
            <Price test:Currency='GBP'>124.00</Price>
        </Item>
    </root>";
    var doc = XDocument.Parse(xml);
    XNamespace test = "urn:my-test-urn";
    
    //get all elements in specific namespace and remove
    doc.Descendants()
       .Where(o => o.Name.Namespace == test)
       .Remove();
    //get all attributes in specific namespace and remove
    doc.Descendants()
       .Attributes()
       .Where(o => o.Name.Namespace == test)
       .Remove();
    
    //print result
    Console.WriteLine(doc.ToString());
    

    输出:

    <root xmlns:test="urn:my-test-urn">
      <Item name="Item one">
        <Price>124.00</Price>
      </Item>
    </root>
    

    【讨论】:

    • 这没有回答问题 - 属性 test:Currency 仍然存在。 .Descendants返回 XElement 的可枚举。
    • @MattJones 抱歉,我确实错过了问题的那一部分,已修复。遍历元素,然后遍历属性看起来并不难阅读,恕我直言。你怎么看?
    • 当然,你在.Descendants()的返回值上使用了.Attributes()!这很好用,我喜欢可读性,而且当我看到你写的东西时,我真的拍了拍我的额头。看到那样,我现在不敢相信我没有意识到我自己可以做到这一点!非常感谢。
    【解决方案2】:

    试试这个。我必须从根元素中提取命名空间,然后运行两个单独的 Linq:

    1. 删除带有命名空间的元素
    2. 使用命名空间删除属性

    代码:

    string xml = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" +
        "<root xmlns:test=\"urn:my-test-urn\">" +
        "<Item name=\"Item one\">" +
        "<test:AlternativeName>Another name</test:AlternativeName>" +
        "<Price test:Currency=\"GBP\">124.00</Price>" +
        "</Item>" +
        "</root>";
    
    XDocument xDocument = XDocument.Parse(xml);
    if (xDocument.Root != null)
    {
        string namespaceValue = xDocument.Root.Attributes().Where(a => a.IsNamespaceDeclaration).FirstOrDefault().Value;
    
        // Removes elements with the namespace
        xDocument.Root.Descendants().Where(d => d.Name.Namespace == namespaceValue).Remove();
    
        // Removes attributes with the namespace
        xDocument.Root.Descendants().ToList().ForEach(d => d.Attributes().Where(a => a.Name.Namespace == namespaceValue).Remove());
    
        Console.WriteLine(xDocument.ToString());
    }
    

    结果:

    <root xmlns:test="urn:my-test-urn">
      <Item name="Item one">
        <Price>124.00</Price>
      </Item>
    </root>
    

    如果要从根元素中删除命名空间,请在获取命名空间值后在 if 语句中添加这一行

    xDocument.Root.Attributes().Where(a => a.IsNamespaceDeclaration).Remove();
    

    结果:

    <root>
      <Item name="Item one">
        <Price>124.00</Price>
      </Item>
    </root>
    

    【讨论】:

    • 它确实有效,但我有点喜欢@har07 的问题中的 LINQ,但有一个 +1 表示想出一个变体。非常感谢。