C#::XmlSerialzierでStringをデシリアライズすると空白が消えてしまうんですけど!

XMLの仕様です。属性値でない普通の文字列は、全改行を空白変換し、二つ以上続く場合は一つに纏められます。
これに対処する方法は二つ。XmlSerialzierでデシリアライズするときに空白をそのまま読み込むように設定すること。より具体的には、Deserializeに渡す引数をstringやstreamでなく、XmlDocumentにしてPreserveSpaceプロパティを弄ること。
普通はこれでいいんだけども、この方法の一番マズいところは"XML的に正しくない"ところ。空白を読み飛ばすかどうかを読む人が判断しなければならない点。
XMLでは通常そのために(空白を保存しなければならない)文字列はCDATAセクションと呼ばれる特殊なセクションとしてマークアップしなければならないのである!
というわけで、stringの代わりに使えばシリアライズ時にCDATAセクションとしてシリアライズされるクラスを書いてみたよ。
特に難しいことをやっているわけではないので、コメントを読んで把握してください。stringとのimplicitな相互変換を実装してあるので、単純にstring型で宣言されているところをこのクラスで置き換えることができます。

/// <summary>
/// CDATAセクションとしてシリアライズされる文字列
/// </summary>
/// <remarks>
/// XMLSerializerによって、改行を含む空白文字を表記するためにCDATAセクションとして出力される領域を示す。
/// stringとの暗黙的な変換が行われる。
/// </remarks>
public class CDATA : IXmlSerializable
{
    /// <summary>禁止文字列(CDATA終端)</summary>
    private const string cdataTerm = "]]>";         

    /// <summary>内部表現データ</summary>
    /// <remarks>
    /// "&lt;![CDATA["を含まない、プレーンなテキストである。
    /// 改行コードは通常通りが望ましいが、代入時には特にチェックしない。
    /// ただし、ReadXmlによるロード時には、処理系の改行コードに合わされる。
    /// </remarks>
    private string text;

    /// <summary>コンストラクタ</summary>
    public CDATA()
    {
        text = "";
    }

    /// <summary>コンストラクタ</summary>
    /// <param name="text">格納する文字</param>
    public CDATA(string text)
    {
        this.text = text;
    }

    /// <summary>stringからの暗黙的キャスト</summary>
    /// <param name="rhs">右辺値</param>
    /// <returns>変換後のオブジェクト</returns>
    public static implicit operator CDATA(string rhs)
    {
        return new CDATA(rhs);
    }

    /// <summary>stringへの暗黙的キャスト</summary>
    /// <param name="rhs">右辺値</param>
    /// <returns>変換後のオブジェクト</returns>
    public static implicit operator string(CDATA rhs)
    {
        return rhs.text;
    }

    /// <summary>文字列変換</summary>
    /// <returns>内部表現をそのまま返す</returns>
    public override string ToString()
    {
        return text;
    }

    #region IXmlSerializable メンバ

    /// <summary>スキーマ取得</summary>
    /// <returns>NULL(スキーマは定義しない)</returns>
    public System.Xml.Schema.XmlSchema GetSchema()
    {
        return null;
    }

    /// <summary>XML読み出し</summary>
    /// <param name="reader">XMLリーダー</param>
    /// <remarks>
    /// XMLの仕様により、CDATAセクションといえども改行コードはLFに変更されてしまうので、
    /// CRLF(正確には環境で定義された改行コード)に戻す。
    /// </remarks>
    public void ReadXml(System.Xml.XmlReader reader)
    {
        text = reader.ReadElementString().Replace("\n", Environment.NewLine);
    }

    /// <summary>XML書き出し</summary>
    /// <param name="writer">XMLライター</param>
    /// <remarks>
    /// データをCDATAセクションとして出力する。
    /// 空文字列の場合はセクションも出力しない(親要素は空要素となる)。
    /// CDATAセクションにCDATA終端文字(]]&gt;)を含めることができないため、
    /// 内部データ(text)に終端文字が含まれる場合は複数のCDATAセクションに分けて出力される。
    /// </remarks>
    public void WriteXml(System.Xml.XmlWriter writer)
    {
        if (text != "")
        {
            var texts = text.Split(new string[] { cdataTerm }, StringSplitOptions.None);
            int i = 0;
            for (i = 0; i < texts.Length - 1; i++)
            {
                writer.WriteCData(texts[i]);
                writer.WriteString(cdataTerm);
            }
            writer.WriteCData(texts[i]);
        }
    }

    #endregion
}