删除空XML标记

时间:2011-09-06 10:28:20

标签: c# .net xml linq-to-xml

我正在寻找一种可以有效地从XML中删除空标签的好方法。您有什么推荐的吗?正则表达式?的XDocument? XmlTextReader的?

例如,

const string original = 
    @"<?xml version=""1.0"" encoding=""utf-16""?>
    <pet>
        <cat>Tom</cat>
        <pig />
        <dog>Puppy</dog>
        <snake></snake>
        <elephant>
            <africanElephant></africanElephant>
            <asianElephant>Biggy</asianElephant>
        </elephant>
        <tiger>
            <tigerWoods></tigerWoods>       
            <americanTiger></americanTiger>
        </tiger>
    </pet>";

可能成为:

const string expected = 
    @"<?xml version=""1.0"" encoding=""utf-16""?>
        <pet>
        <cat>Tom</cat>
        <dog>Puppy</dog>        
        <elephant>                                              
            <asianElephant>Biggy</asianElephant>
        </elephant>                                 
    </pet>";

6 个答案:

答案 0 :(得分:27)

将原件加载到XDocument并使用以下代码提供所需的输出:

var document = XDocument.Parse(original);
document.Descendants()
        .Where(e => e.IsEmpty || String.IsNullOrWhiteSpace(e.Value))
        .Remove();

答案 1 :(得分:15)

这是对处理属性的已接受答案的改进:

XDocument xd = XDocument.Parse(original);
xd.Descendants()
    .Where(e => (e.Attributes().All(a => a.IsNamespaceDeclaration || string.IsNullOrWhiteSpace(a.Value))
            && string.IsNullOrWhiteSpace(e.Value)
            && e.Descendants().SelectMany(c => c.Attributes()).All(ca => ca.IsNamespaceDeclaration || string.IsNullOrWhiteSpace(ca.Value))))
    .Remove();

这里的想法是在删除元素之前检查元素上的所有属性是否也为空。 还有空后代可以具有非空属性的情况。我插入了第三个条件来检查元素在其后代中是否具有所有空属性。考虑以下文档添加了node8

<root>
  <node />
  <node2 blah='' adf='2'></node2>
  <node3>
    <child />
  </node3>
  <node4></node4>
  <node5><![CDATA[asdfasdf]]></node5>
  <node6 xmlns='urn://blah' d='a'/>
  <node7 xmlns='urn://blah2' />
  <node8>
     <child2 d='a' />
  </node8>
</root>

这将成为:

<root>
  <node2 blah="" adf="2"></node2>
  <node5><![CDATA[asdfasdf]]></node5>
  <node6 xmlns="urn://blah" d="a" />
  <node8>
    <child2 d='a' />
  </node8>
</root>

此问题的原始和改进的回答将丢失node2node6以及node8个节点。如果您只想删除e.IsEmpty之类的节点,那么检查<node />会有效,但如果您同时选择<node /><node></node>,则会更加重要。如果您还需要删除空属性,则可以执行以下操作:

xd.Descendants().Attributes().Where(a => string.IsNullOrWhiteSpace(a.Value)).Remove();
xd.Descendants()
  .Where(e => (e.Attributes().All(a => a.IsNamespaceDeclaration))
            && string.IsNullOrWhiteSpace(e.Value))
  .Remove();

会给你:

<root>
  <node2 adf="2"></node2>
  <node5><![CDATA[asdfasdf]]></node5>
  <node6 xmlns="urn://blah" d="a" />
</root>

答案 2 :(得分:2)

一如既往,这取决于您的要求。

您知道空标签的显示方式吗? (例如<pig /><pig></pig>等)我通常不建议使用正则表达式(它们非常有用但同时它们是邪恶的)。除非你的XML没有某种结构,否则考虑string.Replace方法似乎也有问题。

最后,我建议使用XML解析器方法(确保您的代码是有效的XML)。

var doc = XDocument.Parse(original);
var emptyElements = from descendant in doc.Descendants()
                    where descendant.IsEmpty || string.IsNullOrWhiteSpace(descendant.Value)
                    select descendant;
emptyElements.Remove();

答案 3 :(得分:0)

如果我们谈论性能(它提供对XML的快速,仅向前访问),XmlTextReader是更可取的。您可以使用XmlReader.IsEmptyElement属性确定标记是否为空。

产生所需输出的XDocument方法:

public static bool IsEmpty(XElement n)
{
    return n.IsEmpty 
        || (string.IsNullOrEmpty(n.Value) 
            && (!n.HasElements || n.Elements().All(IsEmpty)));
}

var doc = XDocument.Parse(original);
var emptyNodes = doc.Descendants().Where(IsEmpty);
foreach (var emptyNode in emptyNodes.ToArray())
{
    emptyNode.Remove();
}

答案 4 :(得分:0)

您使用的任何内容都必须至少传递一次文件。如果它只是一个你知道的命名标签,那么正则表达式是你的朋友,否则使用堆栈方法。从父标记开始,如果它有子标记,则将其放在堆栈中。如果你发现一个空标签将其删除,那么一旦你通过子标签并到达堆栈顶部的结尾标签,然后弹出并检查它。如果它是空的也删除它。这样您就可以删除所有空标记,包括带有空子标记的标记。

如果您使用this

,请使用注册表达式

答案 5 :(得分:0)

XDocument可能最容易实现,并且如果您知道您的文档相当小,则会提供足够的性能。

在处理非常大的文档时,

XmlTextReader将比XDocument更快并且使用更少的内存。

正则表达式最适合处理文本而不是XML。它可能无法按照您的意愿处理所有边缘情况(例如CDATA部分中的标记;具有xmlns属性的标记),因此对于一般实现可能不是一个好主意,但可能是足够的,具体取决于您控制多少有输入XML。