2010年03月26日

いちいち名前空間宣言しなくてもいいです

誰もが思いつくような小ネタを一つ。

System.Runtime.Serialization.DataContarctSerializer クラスは WCF 用に用意されたシリアライザですが、特徴の一つに、同一オブジェクトを参照としてシリアライズし、同じオブジェクトとしてデシリアライズすることが可能という機能があります。

// このクラスをシリアライズしてみる
[DataContract(Name="Pack")] public class Pack {
    [DataMember(Name="Contents")] public List<Content> Contents { get; private set; }
    public Pack() {
        this.Contents = new List<Content>();
    }
}
[DataContract(Name="Content", IsReference=true)] public class Content {
    [DataMember(Name="Name")] public string Name { get; set; }
}
static void Main() {
    var pack = new Pack();
    var content = new Content() { Name = "A" };
    // 同じオブジェクトを Add する
    pack.Contents.Add(content);
    pack.Contents.Add(content);
    var serializer = new DataContractSerializer(typeof(Pack));
    var writer = new StringWriter(); // XML の出力先
    var settings = new XmlWriterSettings() { Indent = true };
    using (var xml = XmlWriter.Create(writer, settings)) {
        serializer.WriteObject(xml, pack);
    }
    var reader = XmlReader.Create(new StringReader(writer.ToString()));
    pack = (Pack)serializer.ReadObject(reader);
    Debug.WriteLine(Object.ReferenceEquals(pack.Contents[0], pack.Contents[1]));
    Debug.WriteLine(writer);
}

実行結果
True
<?xml version="1.0" encoding="utf-16"?>
<Pack xmlns:i="http://www.w3.org/2001/XMLSchema-instance"
    xmlns="http://schemas.datacontract.org/2004/07/HongliangSoft.Samples">
  <Contents>
    <Content z:Id="i1" xmlns:z="http://schemas.microsoft.com/2003/10/Serialization/">
      <Name>A</Name>
    </Content>
    <Content z:Ref="i1" xmlns:z="http://schemas.microsoft.com/2003/10/Serialization/" />
  </Contents>
</Pack>

Add した content が同一オブジェクトなので、Object.ReferenceEquals が True を返します。もちろん、Content.Name が同じ値であっても別インスタンスであればちゃんと別オブジェクトとしてシリアライズされます。

Content クラスの DataContract 属性に付けた IsReference プロパティが同一オブジェクトを参照としてシリアライズするかどうかを決定します。既定値は false です。

さて、それはいいんですが、この出力された XML を見て気になるのが一点。Content 要素に全部 xmlns:z 属性による名前空間の宣言が入っています。これでは見づらいしデータが嵩張ります。ルート要素で一度宣言すればそれ以外は完全に不要なはずですが、残念ながら DataContract 属性や DataContractSerializer クラスにそれを制御するプロパティは見あたりません。

そこで、自前で設定してしまいましょう。

// 書き込み部分だけ変更
// serializer.WriteObject(xml, pack);
serializer.WriteStartObject(xml, pack);
xml.WriteAttributeString("xmlns", "z", "http://www.w3.org/2000/xmlns/",
    "http://schemas.microsoft.com/2003/10/Serialization/");
serializer.WriteObjectContent(xml, pack);
serializer.WriteEndObject(xml);

実行結果
True
<?xml version="1.0" encoding="utf-16"?>
<Pack xmlns:z="http://schemas.microsoft.com/2003/10/Serialization/"
    xmlns:i="http://www.w3.org/2001/XMLSchema-instance"
    xmlns="http://schemas.datacontract.org/2004/07/HongliangSoft.Samples">
  <Contents>
    <Content z:Id="i1">
      <Name>A</Name>
    </Content>
    <Content z:Ref="i1" />
  </Contents>
</Pack>

ルート要素に xmlns:z 属性が定義されていれば、DataContractSerializer もちゃんとそれを理解して、Content 属性には一々付けなくなります。もちろんデシリアライズも正しく行われます。

posted by Hongliang at 19:47| Comment(0) | TrackBack(0) | C# | このブログの読者になる | 更新情報をチェックする
この記事へのコメント
コメントを書く
お名前:

メールアドレス:

ホームページアドレス:

コメント:

認証コード: [必須入力]


※画像の中の文字を半角で入力してください。
この記事へのトラックバックURL
http://blog.seesaa.jp/tb/144748540

この記事へのトラックバック

ここ(hongliang.seesaa.net)で公開しているものについて、利用は自由に行って頂いて構いません。改変、再頒布もお好きになさって下さい。利用に対しこちらが何かを要求することはありません。

ただし、公開するものを使用、または参考したことによって何らかの損害等が生じた場合でも、私はいかなる責任も負いません。

あ、こんなのに使ったってコメントを頂ければ嬉しいです。

×

この広告は1年以上新しい記事の投稿がないブログに表示されております。