none
Collection(Of Interface) のXmlSerializerでのシリアライズができません RRS feed

  • 質問

  • はじめまして

    タイトルの件で悩んでいます。

    インターフェースを列挙したコレクションをxmlでシリアライズしようとするとエラーが出ます。

    {"インターフェイス ConsoleApplication1.hoge をシリアル化できません。"}

    BinaryFormatterではできました。

    インターフェースではなく派生クラスではXmlArrayItem属性をつけることによりxmlでシリアル化はできたのですが

    どうしてもインターフェースのコレクションをxmlでシリアル化したいです。

    なにかよい方法はありますでしょうか?

    できる限りスマートな方法で...

    よいアドバイスをお待ちしております。

    2012年5月15日 0:06

回答

  • インターフェースに対してXmlInclude属性を付け、どのクラスが出現しうるか明示します。もしくはXmlSerializerコンストラクターの第2引数に追加のクラスを与えてシリアル化させます。

    インターフェイスに対しては無力だったような。(XmlInclude属性はAttributeTargetsにInterfaceを含みますが)

    インターフェイスのメンバを持つクラスにIXmlSerializeインターフェイスを実装させて、シリアライズ処理そのものを自前で書いてしまう方法もありますね。

    • 回答としてマーク momo1995 2012年5月15日 5:26
    2012年5月15日 1:35

すべての返信

  • ご存知と思いますが、インターフェースをnewして新たなオブジェクトを生成することはできません。ですので、XmlSerializerはインターフェースを逆シリアル化できません。対称性からシリアル化もできません。BinaryFormatterの場合、どのクラスをシリアル化したのかが記録されるため、逆シリアル化可能であり、そのためにシリアル化も可能になっています。

    対策としては、インターフェースではなく直接のクラスをシリアル化させます。試していませんが方法はいくつかあり、インターフェースに対してXmlInclude属性を付け、どのクラスが出現しうるか明示します。もしくはXmlSerializerコンストラクターの第2引数に追加のクラスを与えてシリアル化させます。

    2012年5月15日 1:16
  • インターフェースに対してXmlInclude属性を付け、どのクラスが出現しうるか明示します。もしくはXmlSerializerコンストラクターの第2引数に追加のクラスを与えてシリアル化させます。

    インターフェイスに対しては無力だったような。(XmlInclude属性はAttributeTargetsにInterfaceを含みますが)

    インターフェイスのメンバを持つクラスにIXmlSerializeインターフェイスを実装させて、シリアライズ処理そのものを自前で書いてしまう方法もありますね。

    • 回答としてマーク momo1995 2012年5月15日 5:26
    2012年5月15日 1:35
  • インターフェイスに対しては無力だったような。(XmlInclude属性はAttributeTargetsにInterfaceを含みますが)
    参考になります。やはりそうでしたか。Interfaceを含んでいたのでできるかも?と思いつつ「試していませんが」と書きました。
    2012年5月15日 1:43
  • なるほど、インターフェースのコレクションはxmlではシリアライズできないのですね。

    BinaryFormatterで我慢します。

    みなさんありがとうございました。

    2012年5月15日 5:30
  • 今更ですが、私も質問者さまと同じことをやりたくて試してみました。検索でここがHitしやすいようなので、同じ問題を抱えてる方の参考になればと思います。

    やっていることは「インターフェースを含むコレクションにIXmlSerializeを実装」しているだけです。あまりスマートではないかもしれませんが、他の手はないような。

    以下、Listで実装した例です。

    using System;
    using System.Collections.Generic;
    using System.Xml;
    using System.Xml.Schema;
    using System.Xml.Serialization;

    namespace Hogehoge
    {
        public class SerializeableList : List<IHoge>, IXmlSerializable
        {
            public XmlSchema GetSchema()
            {
                return null;
            }

            public void ReadXml(XmlReader reader)
            {
                // itemがない場合なにもしない
                if (reader.IsEmptyElement)
                {
                    return;
                }

                // item要素に進める
                reader.ReadToDescendant("item");
                do
                {
                    // 属性から型名を取得
                    string typeName = reader.GetAttribute("Type");

                    // itemの中身に進める
                    reader.Read();

                    // 型名のXmlSerializerを生成し、デシリアライズ
                    XmlSerializer xs = new XmlSerializer(Type.GetType(typeName));
                    IHoge item = (IHoge)xs.Deserialize(reader);

                    // Listに追加
                    this.Add(item);

                } while (reader.ReadToNextSibling("item"));
                // 次のitem要素があれば処理

                // Listの終了タグへ
                reader.ReadEndElement();
            }

            public void WriteXml(XmlWriter writer)
            {
                // Listの要素を全て処理
                foreach(IHoge item in this)
                {
                    // item要素を生成し、属性に型名を書き出す
                    writer.WriteStartElement("item");
                    writer.WriteAttributeString("Type", item.GetType().ToString());

                    // itemの型でXmlSerializerを生成し、シリアライズ
                    XmlSerializer xs = new XmlSerializer(item.GetType());
                    xs.Serialize(writer, item);

                    // item要素を閉じる
                    writer.WriteEndElement();
                }
            }
        }
    }

    2012年8月10日 22:46