我正在寻找一种可以有效地从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>";
答案 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>
此问题的原始和改进的回答将丢失node2
和node6
以及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。