2012-01-10 5 views
6

C#とLinq-to-XMLを使用してXMLファイルを修復する(つまり、欠落していた属性/値を挿入する)ツールを作成しました。このツールは、既存のXMLファイルをXDocumentオブジェクトに読み込みます。次に、欠落しているデータを挿入するためにノードを解析します。その後、XDocument.Save()を呼び出して変更を別のディレクトリに保存します。XDocument.Save()は 個のエンティティを削除します

すべてのことは、1つのことを除いては問題ありません:任意& #xA; XMLファイルのテキスト内にあるエンティティは、改行文字で置き換えられます。もちろん、エンティティは新しい行を表しますが、別のコンシューマがXMLを必要とするため、エンティティをXMLに保存する必要があります。

変更されたXDocumentを失うことなく保存する方法はありますか& #xA;エンティティ

ありがとうございます。

+1

古い文書を読み込むときまたは新しい文書を保存するときに が置き換えられますか? –

+0

@Arnold:新しいものを保存するとき。 – mahdaeng

+0

理想的なソリューションは、XMLのコンシューマを修正して、XMLを適切に処理することです。 – svick

答えて

10


エンティティは、技術的にはXMLの「数値参照」と呼ばれ、元のドキュメントがXDocumentに読み込まれると解決されます。 XDocumentがロードされた後に解決された空白のエンティティと、わずかな空白(通常はプレーンテキストのビューアのXMLドキュメントの書式設定に使用される)とを区別する方法がないため、問題を解決するには問題があります。したがって、あなたの文書に余分な空白がない場合にのみ以下が適用されます。

System.Xmlライブラリでは、クラスのNewLineHandlingプロパティをEntitizeに設定することで、空白のエンティティを維持することができます。しかし、テキストノード内では、\rから
までとなり、\nから
にはなりません。

最も簡単な解決策は、XmlWriterクラスから派生し、そのWriteStringメソッドをオーバーライドして、手動で空白文字を数字のエンティティに置き換えることです。 WriteString方法はまた、.NETは、それぞれ&amp;&lt;、及び&gt;にエンティティ化された構文マーカー&<、及び>、テキスト・ノードに表示される許可されていない文字をentitizes場所であることを起こります。

XmlWriterは抽象であるため、前のクラスのすべての抽象メソッドを実装する必要がないように、XmlTextWriterから派生します。ここで間に合わせと-実装です:本番環境での使用を目的とした場合

public class EntitizingXmlWriter : XmlTextWriter 
{ 
    public EntitizingXmlWriter(TextWriter writer) : 
     base(writer) 
    { } 

    public override void WriteString(string text) 
    { 
     foreach (char c in text) 
     { 
      switch (c) 
      { 
       case '\r': 
       case '\n': 
       case '\t': 
        base.WriteCharEntity(c); 
        break; 
       default: 
        base.WriteString(c.ToString()); 
        break; 
      } 
     } 
    } 
} 

、それは非常に非効率的なので、あなたは、c.ToString()一部を廃止したいと思います。オリジナルのtextのサブストリングをバッチ処理して、エンタイアしたい文字を含まないコードを最適化し、それらを1つのbase.WriteStringコールにまとめてフィードすることができます。

警告の単語ベースWriteString方法は、それによって\r&amp;#xA;に拡張させる、&amp;で任意&文字を置き換えることになるので、次のナイーブな実装では、動作しません。このことができます

using (var textWriter = new StreamWriter(destination)) 
using (var xmlWriter = new EntitizingXmlWriter(textWriter)) 
    document.Save(xmlWriter); 

希望:

public override void WriteString(string text) 
    { 
     text = text.Replace("\r", "&#xD;"); 
     text = text.Replace("\n", "&#xA;"); 
     text = text.Replace("\t", "&#x9;"); 
     base.WriteString(text); 
    } 

最後に、ちょうど次のスニペットを使用し、先のファイルまたはストリームにあなたのXDocumentを保存します!

編集:参考のために、ここで上書きWriteString方法の最適化バージョンは、次のとおりです。

public override void WriteString(string text) 
{ 
    // The start index of the next substring containing only non-entitized characters. 
    int start = 0; 

    // The index of the current character being checked. 
    for (int curr = 0; curr < text.Length; ++curr) 
    { 
     // Check whether the current character should be entitized. 
     char chr = text[curr]; 
     if (chr == '\r' || chr == '\n' || chr == '\t') 
     { 
      // Write the previous substring of non-entitized characters. 
      if (start < curr) 
       base.WriteString(text.Substring(start, curr - start)); 

      // Write current character, entitized. 
      base.WriteCharEntity(chr); 

      // Next substring of non-entitized characters tentatively starts 
      // immediately beyond current character. 
      start = curr + 1; 
     } 
    } 

    // Write the trailing substring of non-entitized characters. 
    if (start < text.Length) 
     base.WriteString(text.Substring(start, text.Length - start)); 
} 
+0

これは私が今までに見た中で最も深い答えの一つです。私はこれを試してみるつもりです。それがうまくいかなくても(それはおそらく)、あなたは私の投票権を得ます。ありがとう、ダグラス! – mahdaeng

+0

あなたは大歓迎です:-)上記のことは、ソースXMLにわずかな空白がない場合にのみ機能することを忘れないでください。わずかな空白がある場合は、他の回答のコードを使用することをお勧めします(下記参照)。 – Douglas

0

ドキュメントはあなたの&#xA;エンティティと区別したい無意味な空白が含まれている場合は、以下を使用することができます(はるかに簡単です)解決策:&#xA;の文字参照を一時的に他の文字(文書にはまだ存在しない文字)に変換し、XML処理を実行してから、その文字を出力結果に戻します。以下の例では、私たちは私的な文字U+E800を使用します。

static string ProcessXml(string input) 
{ 
    input = input.Replace("&#xA;", "&#xE800;"); 
    XDocument document = XDocument.Parse(input); 
    // TODO: Perform XML processing here. 
    string output = document.ToString(); 
    return output.Replace("\uE800", "&#xA;"); 
} 

XDocumentは、対応するUnicode文字に数値文字参照を解決するため、"&#xE800;"実体が出力に'\uE800'に解決されていたであろう、ということに注意してください。

通常、Unicodeの「私的使用領域」(U+E000 - U+F8FF)のコードポイントは安全に使用できます。特別な安全を望む場合は、文字が文書にまだ存在していないことを確認してください。そうであれば、前記範囲から別の文字を選択する。キャラクターは一時的かつ内部的にのみ使用されるため、どちらを使用するかは関係ありません。すべてのプライベート使用文字がドキュメントに既に存在するという非常にありそうなシナリオでは、例外をスローします。しかし、私はそれが実際には起こることはないと思う。

関連する問題