2011-09-06 97 views
12

XMLから空のタグを効率的に削除できる優れた方法を探しています。何がお勧めですか?正規表現? XDocument? XmlTextReader?空のXMLタグを削除する

例えば、

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>"; 
+1

カワイイをあなたも空の属性を削除する必要がある場合は、これを行うことができます!正規表現ではない! – JXG

+0

私は昨日、単純なパーフェクマンステストを行いました.XDocumentはパフォーマンスの点で正規表現よりはるかに優れていますが、XmlTextReaderを使用してそれを実装する方法はまだまだ不十分です。複雑さの点でXDocumentは十分です私はXDocumentのために行く、すべてのあなたの助けてくれてありがとう! – Ming

+0

これは役立ちますhttp://stackoverflow.com/questions/14509188/remove-empty-blanks-elements-in-collection-of-xml-nodes –

答えて

25

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

これは素晴らしい答えですが、属性は含まれていてもコンテンツは含まれていない要素が削除されます。例えば、 'は削除されます。私はこれを補うためにこれに基づいて別の答えを出しました。 –

+0

@DanField古い質問ですが、最新の回答やより良い回答を追加するのに役立ちます。もしあなたが好きだったら、私の答えを更新することもできました。とにかく私はあなたの答えをupvoted。 – Jamiec

0

XmlTextReaderが好適である(それはXMLへの高速、前方のみのアクセスを提供します)。 XmlReader.IsEmptyElementプロパティを使用して、タグが空であるかどうかを判断できます。

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(); 
} 
+1

要素がの場合、IsEmptyElementは機能しません。要素が Ming

+0

@Mingであれば動作しますが、XDocumentの場合と同じロジックを実装できます。 –

0

使用する何かが少なくとも一度ファイルを通過する必要があります:所望の出力を生成し

XDocumentアプローチ。あなたが知っているregexという単なる名前付きタグがあなたの友人なら、スタックアプローチを使用してください。親タグで開始し、サブタグがある場合はスタックに配置します。空のタグが見つかった場合は、それを削除してから、子タグを通過して、スタックの上にあるものの終了タグに達したら、それをポップしてチェックしてください。空の場合はそれも削除します。これにより、空の子を持つタグを含むすべての空のタグを削除できます。

REGの元の式がthis

0

XDocumentおそらく実装が最も簡単で、あなたの文書が適度に小さいことを知っている場合、十分なパフォーマンスが得られますを使用した後、あなたがしている場合。

XmlTextReaderは、非常に大きなドキュメントを処理する場合、XDocumentよりも高速でメモリを少なくします。

正規表現は、XMLではなくテキストを処理するのに最適です。あなたが望むように(CDATAセクション内のタグ、xmlns属性を持つタグなど)、すべてのエッジケースを処理しない可能性がありますので、一般的な実装には適していませんが、入力XMLを持っています。

+0

ありがとう、私はXmlTextReaderが好きです。私はそれをabitで遊んでいますが、私の要求を達成する方法を見つけ出すことができます。あなたはそれのための例を持っていますか? – Ming

+1

@Mingを参照してください。次のMSDNの記事では、XmlReaderをXmlWriterに連結する方法について説明します。これは、必要な方法でXMLをフィルタリングできる手法です。http://msdn.microsoft.com/en-us/library/ -us/library/aa302289.aspx – Joe

2

いつものように、それはあなたの要求によって異なります。

空タグの表示方法をご存知ですか? (例:<pig /><pig></pig>など)通常は正規表現の使用をお勧めしません(本当に便利ですが、同時に悪いです)。また、あなたのXMLが特定の構造を持たない限り、string.Replaceのアプローチは問題になるようです。

最後に、XMLパーサーのアプローチを使用することをお勧めします(コードが有効なXMLであることを確認してください)。

XDocumentにあなたの元をロードし、次のコードを使用すると、ご希望の出力を与える
var doc = XDocument.Parse(original); 
var emptyElements = from descendant in doc.Descendants() 
        where descendant.IsEmpty || string.IsNullOrWhiteSpace(descendant.Value) 
        select descendant; 
emptyElements.Remove(); 
+1

余分な 'ForEach'と' Remove'は必要ありません。removeメソッドは、IEnumerableのすべての要素に対して機能します。 – Jamiec

+0

「エラー」が見つかりました。編集、ありがとう:) –

+0

実際に解決策を受け入れる答えよりも早く提供するための+1です。これはもう少しエレガントなバージョンです。 –

14

これは、属性を処理するために受け入れ答えの改善であることを意味する:

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

ここの考え方は、要素のすべての属性も削除する前に空であることを確認することです。 空の子孫が空でない属性を持つ場合もあります。要素の子孫の中に空の属性がすべてあることを確認するために3番目の条件を挿入しました。 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> 

とこの質問への答えはnode2node6node8ノードを失うことになる改善しました。 <node />のようなノードだけを取り除きたい場合はe.IsEmptyをチェックしますが、<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> 
関連する問題