删除XML节点集合中的空元素/空白元素

15

我有一个像这样的XML文档:

<magento_api>
    <data_item>
        <code>400</code>
        <message>Attribute weight is not applicable for product type Configurable Product</message>
    </data_item>
    <data_item>
        <code>400</code>
        <message>Resource data pre-validation error.</message>
    </data_item>
    <data_item>
        <code>1</code>
        <message></message>
    </data_item>
    <data_item>
        <code></code>
        <message>No code was given</message>
    </data_item>
</magento_api>

我想要遍历每个节点并执行以下操作:

  1. 丢弃任何空/空白元素。
  2. 生成仅包含值的新节点。
  3. 将结果文档发送到不同的Web服务。

我困惑的部分是如何遍历每个节点并检查每个元素是否为空值。

我一直在 http://rextester.com/runcode 上测试此代码,但似乎无法解决:

Console.WriteLine("Querying tree loaded with XElement.Load");
Console.WriteLine("----");
XElement doc = XElement.Parse(@"<magento_api>
          <data_item>
            <code>400</code>
            <message>Attribute weight is not applicable for product type Configurable Product</message>
          </data_item>
          <data_item>
            <code>400</code>
            <message>Resource data pre-validation error.</message>
          </data_item>
          <data_item>
            <code>1</code>
            <message></message>
          </data_item>
          <data_item>
            <code></code>
            <message>No code was given</message>
          </data_item>
    </magento_api>");

int counter = 1;
IEnumerable<XNode> nodes =
    from nd in doc.Nodes()
    select nd;
foreach (XNode node in nodes)
{
    Console.WriteLine(counter + "-" + node);
    IEnumerable<XElement> elements =
    from el in node //this is where I've been trying various methods, but no dice.
    select el;
    foreach (XElement e in elements)
    {
           Console.WriteLine(counter + "-" + e.Name + "-" + e.Value + "\r\n");
    }
    counter++;
}

基于上述XML输入,我希望获得以下输出:

<magento_api>
    <data_item>
        <code>400</code>
        <message>Attribute weight is not applicable for product type Configurable Product</message>
    </data_item>
    <data_item>
        <code>400</code>
        <message>Resource data pre-validation error.</message>
    </data_item>
    <data_item>
        <code>1</code>
    </data_item>
    <data_item>
        <message>No code was given</message>
    </data_item>
</magento_api>

我不确定我是否正在使用正确的方法来迭代节点和元素。


1
“元素是 NULL” 是什么意思?此外请注意,你在这里没有任何目的使用查询表达式 - 例如,你可以只使用 node 而不是写成 from el in node select el - Jon Skeet
@JonSkeet - 我的意思是指空白/空的元素。这不就等同于NULL吗? - jared
实际上,在XML中并没有“NULL”这样的概念。而且,您期望返回什么结构也不清楚。如果您能编辑问题并提供所需的输入文件输出,那将非常有用。 - Jon Skeet
3个回答

40
一个单行代码就能完成任务,不需要遍历所有元素。如下所示:

一行代码搞定:

doc.Descendants().Where(e => string.IsNullOrEmpty(e.Value)).Remove();

测试员

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Linq;

namespace ConsoleApplication1
{
    public class TestRemove
    {
        public static void Main() {
            Console.WriteLine("----OLD TREE STARTS---");
            XElement doc = XElement.Parse(@"<magento_api>
                                              <data_item>
                                                <code>400</code>
                                                <message>Attribute weight is not applicable for product type Configurable Product</message>
                                              </data_item>
                                              <data_item>
                                                <code>400</code>
                                                <message>Resource data pre-validation error.</message>
                                              </data_item>
                                              <data_item>
                                                <code>1</code>
                                                <message></message>
                                              </data_item>
                                              <data_item>
                                                <code></code>
                                                <message>No code was given</message>
                                              </data_item>
                                        </magento_api>");
            Console.Write(doc.ToString());
            Console.WriteLine("");
            Console.WriteLine("----OLD TREE ENDS---");
            Console.WriteLine("");
            doc.Descendants().Where(e => string.IsNullOrEmpty(e.Value)).Remove();
            Console.WriteLine("----NEW TREE STARTS---");
            Console.Write(doc.ToString());
            Console.WriteLine("");
            Console.WriteLine("----NEW TREE ENDS---");
            Console.ReadKey();
        }
    }
}

同时你也可以在这里进行测试。


1
你需要注意自闭合元素是否具有属性,这很可能不是最终用户的需求。例如:<Reference Include="Microsoft.VisualBasic" />。 - aolszowka
+1 @aolszowka。而且magento_api在最奇怪的地方使用属性。我采取了这种方法来解决相关问题。https://dev59.com/8EzSa4cB1Zd3GeqPquyi#31708077 - Vic Colborn
1
这种方法虽然可以去掉XML定义头。 - Nyerguds
看起来解决方案是使用doc.Save(writer)将其保存回XmlWriter,而不是使用doc.ToString() - Nyerguds
删除一个空元素可能会使父元素为空,因此您可能需要使用递归来解决这个问题。 - Developer Webs

10
doc.Descendants().Where(e => string.IsNullOrEmpty(e.Value)).Remove(); 

这一行不会删除那些只包含空子标签的空父标签,它只会删除它们的子元素,这可能或可能不适合你的情况。要实现这个功能很简单,你只需要从最低层级开始删除即可,类似于:

foreach(XElement child in doc.Descendants().Reverse())
{
    if(!child.HasElements && string.IsNullOrEmpty(child.Value) && !child.HasAttributes) child.Remove();
}

感谢Nyerguds提供的属性建议。


3
尽管如此,您可能需要在if检查中添加&& !child.HasAttributes - Nyerguds

1

在 VB 中,以防我需要再次找到它:

doc.Descendants().Where(Function(e) String.IsNullOrEmpty(e.Value)).Remove()

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接