2011-07-27 10 views
9

を残します。LINQのツーXML XElement.Removeは()私は(TCP/IPを介して受信)バイト配列から作成したXDocumentを持って、不要な空白

私は、特定のXMLノード(XElements)のためにと()XElement.Removeを呼び出すことにより、Xdocumentのオフ値「ポップ」それを取得した後に検索します。私の構文解析のすべてが完了した後、私は私が(XDocumentの残りのXML)を解析しませんでしたXMLをログに記録できるようにしたいです。問題は、XElement.Remove()が呼び出されたときに残る余分な空白があることです。私は、残りのXML形式の残りの部分を維持しながら、この余分な空白を削除するための最良の方法を知ってほしいです。

例/サンプルコード

私はソケットの上に次のXML受け取った場合:

<?xml version="1.0"?> 
<catalog> 
    <book id="bk101"> 
     <author>Gambardella, Matthew</author> 
     <title>XML Developer's Guide</title> 
     <genre>Computer</genre> 
     <price>44.95</price> 
     <publish_date>2000-10-01</publish_date> 
     <description>An in-depth look at creating applications with XML.</description> 
    </book> 
</catalog> 

をそして、私はこのXMLを解析し、XElementsの数を削除するには、次のコードを使用します。

private void socket_messageReceived(object sender, MessageReceivedEventArgs e) 
{ 
    XDocument xDoc; 
    try 
    { 
     using (MemoryStream xmlStream = new MemoryStream(e.XmlAsBytes)) 
     using (XmlTextReader reader = new XmlTextReader(xmlStream)) 
     { 
      xDoc = XDocument.Load(reader); 
     } 

     XElement Author = xDoc.Root.Descendants("author").FirstOrDefault(); 
     XElement Title = xDoc.Root.Descendants("title").FirstOrDefault(); 
     XElement Genre = xDoc.Root.Descendants("genre").FirstOrDefault(); 

     // Do something with Author, Title, and Genre here... 

     if (Author != null) Author.Remove(); 
     if (Title != null) Title.Remove(); 
     if (Genre != null) Genre.Remove(); 

     LogUnparsedXML(xDoc.ToString()); 

    } 
    catch (Exception ex) 
    { 
     // Exception Handling here... 
    } 
} 

LogUnparsedXMLメッセージに送信されるxmlの結果の文字列は次のようになります。

<?xml version="1.0"?> 
<catalog> 
    <book id="bk101"> 



     <price>44.95</price> 
     <publish_date>2000-10-01</publish_date> 
     <description>An in-depth look at creating applications with XML.</description> 
    </book> 
</catalog> 

これは大きな問題のようには見えないかもしれませんが、私の実際のアプリケーションでは、残ったXMLはかなりかすかなようです。私はSaveOptions列挙型を無駄にするXDocument.ToStringオーバーロードを使用しようとしました。また、SaveOptions列挙型を使ってファイルに保存するようにxDoc.Saveを呼び出そうとしました。私は空白を削除しようとするXElement.Nodes().OfType<XText>()を使用し、いくつかの異なるLINQクエリを使って実験してみてくださいでしたが、多くの場合、私はを取り除くためにしようとしています空白と一緒に保存したいの空白を取ってしまいました。

ご協力いただきありがとうございます。

ジョー

+0

'ToString()'のオプションを 'SaveOptions.DisableFormatting'に設定してみてください。 –

答えて

3

ソリューションが重くXDocument.Load()は空白テキストノードを生成する方法に依存するためには、移植性のある形で答えることは容易ではありません(とXMLにLINQのいくつかの実装は、その周りがあるという微妙な詳細については同意しないかもしれません) 。

つまり、<book>の要素からの最後のという子(<description>)を削除することは決してないようです。それは確かにケースの場合、私たちは親要素の終了タグのインデントを心配する必要はありません、と私たちは別の要素に到達するまで、私たちはただの要素とそのすべての以下のテキストノードを削除することができます。 TakeWhile()がその仕事をします。

編集:結局のところ、最後の子を削除する必要があるようです。したがって、物事はより複雑になります。要素は、その親の最後の要素ではない場合

  • ::私たちは次の要素に到達するまで
    • 以下のすべてのテキストノードを削除し、以下のコードは、次のアルゴリズムを実装しています。
    • そうでない場合は
    • 、そのノードのみ改行が含まれている場合、我々は、改行を含むもの、
    • を見つけるまで、以下のすべてのテキストノードを削除します。
      • をそのノードを削除します。
      • そうでない場合は
      • は、元のノードを削除し
      • 、元のノードの後に​​、そのノードを挿入し
      • 、改行後に発見された空白のみを含む新しいノードを作成します。
  • 要素自体を削除します。

結果のコードは次のとおりです。そこから

public static void RemoveWithNextWhitespace(this XElement element) 
{ 
    IEnumerable<XText> textNodes 
     = element.NodesAfterSelf() 
       .TakeWhile(node => node is XText).Cast<XText>(); 
    if (element.ElementsAfterSelf().Any()) { 
     // Easy case, remove following text nodes. 
     textNodes.ToList().ForEach(node => node.Remove()); 
    } else { 
     // Remove trailing whitespace. 
     textNodes.TakeWhile(text => !text.Value.Contains("\n")) 
       .ToList().ForEach(text => text.Remove()); 
     // Fetch text node containing newline, if any. 
     XText newLineTextNode 
      = element.NodesAfterSelf().OfType<XText>().FirstOrDefault(); 
     if (newLineTextNode != null) { 
      string value = newLineTextNode.Value; 
      if (value.Length > 1) { 
       // Composite text node, trim until newline (inclusive). 
       newLineTextNode.AddAfterSelf(
        new XText(value.SubString(value.IndexOf('\n') + 1))); 
      } 
      // Remove original node. 
      newLineTextNode.Remove(); 
     } 
    } 
    element.Remove(); 
} 

、あなたが行うことができます:

if (Author != null) Author.RemoveWithNextWhitespace(); 
if (Title != null) Title.RemoveWithNextWhitespace(); 
if (Genre != null) Genre.RemoveWithNextWhitespace(); 

私はあなたから送られたループのようなもので上記を置き換えることをお勧めしますが配列またはコードの冗長性を避けるためにparamsメソッド呼び出し。

関連する問題