1. ホーム
  2. c#

[解決済み] IXmlSerializableの正しい実装方法?

2022-04-26 12:32:46

質問

プログラマーが IXmlSerializable それを実装するためのルールやベストプラクティスは何でしょうか?という話を聞いたことがあります。 GetSchema() を返すべき nullReadXml は、次の要素に移動してから戻るべきです。これは本当でしょうか?また WriteXml - は、オブジェクトのルート要素を書くべきでしょうか、それともルートはすでに書かれていると仮定されるのでしょうか?子オブジェクトはどのように扱われ、どのように記述されるべきでしょうか?

今あるサンプルはこんな感じです。良い反応があれば更新していきます。

public class MyCalendar : IXmlSerializable
{
    private string _name;
    private bool _enabled;
    private Color _color;
    private List<MyEvent> _events = new List<MyEvent>();


    public XmlSchema GetSchema() { return null; }

    public void ReadXml(XmlReader reader)
    {
        if (reader.MoveToContent() == XmlNodeType.Element && reader.LocalName == "MyCalendar")
        {
            _name    = reader["Name"];
            _enabled = Boolean.Parse(reader["Enabled"]);
            _color   = Color.FromArgb(Int32.Parse(reader["Color"]));

            if (reader.ReadToDescendant("MyEvent"))
            {
                while (reader.MoveToContent() == XmlNodeType.Element && reader.LocalName == "MyEvent")
                {
                    MyEvent evt = new MyEvent();
                    evt.ReadXml(reader);
                    _events.Add(evt);
                }
            }
            reader.Read();
        }
    }

    public void WriteXml(XmlWriter writer)
    {
        writer.WriteAttributeString("Name",    _name);
        writer.WriteAttributeString("Enabled", _enabled.ToString());
        writer.WriteAttributeString("Color",   _color.ToArgb().ToString());

        foreach (MyEvent evt in _events)
        {
            writer.WriteStartElement("MyEvent");
            evt.WriteXml(writer);
            writer.WriteEndElement();
        }
    }
}

public class MyEvent : IXmlSerializable
{
    private string _title;
    private DateTime _start;
    private DateTime _stop;


    public XmlSchema GetSchema() { return null; }

    public void ReadXml(XmlReader reader)
    {
        if (reader.MoveToContent() == XmlNodeType.Element && reader.LocalName == "MyEvent")
        {
            _title = reader["Title"];
            _start = DateTime.FromBinary(Int64.Parse(reader["Start"]));
            _stop  = DateTime.FromBinary(Int64.Parse(reader["Stop"]));
            reader.Read();
        }
    }

    public void WriteXml(XmlWriter writer)
    {
        writer.WriteAttributeString("Title", _title);
        writer.WriteAttributeString("Start", _start.ToBinary().ToString());
        writer.WriteAttributeString("Stop",  _stop.ToBinary().ToString());
    }
}

対応するサンプルXML

<MyCalendar Name="Master Plan" Enabled="True" Color="-14069085">
    <MyEvent Title="Write Code" Start="-8589241828854775808" Stop="-8589241756854775808" />
    <MyEvent Title="???" Start="-8589241828854775808" Stop="-8589241756854775808" />
    <MyEvent Title="Profit!" Start="-8589247048854775808" Stop="-8589246976854775808" />
</MyCalendar>

解決方法は?

はい、GetSchema() は null を返すべきです。 .

IXmlSerializable.GetSchema メソッド This メソッドは予約されているので、使用しないでください。 を使用します。を実装する場合 IXmlSerializable インターフェースでは、次のようにします。 はヌル参照(Nothing)を返します。 Visual Basic)の代わりにこのメソッドを使用します。 カスタムスキーマの指定が必要な場合 が必要な場合は XmlSchemaProviderAttributeを使用します。 クラスがあります。

read、writeともに、object要素はすでに書き込まれているので、writeで外側の要素を追加する必要はありません。例えば、2つの中で属性の読み書きを開始すればいいのです。

について 書く :

WriteXmlの実装は はXMLを書き出す必要があります。 オブジェクトの表現です。そのため フレームワークは、ラッパー要素を記述し は、XML ライターの後に配置されます。 を開始します。あなたの実装では その内容は、子 要素で構成されます。その後、フレームワークは ラッパー要素

そして 読む :

ReadXml メソッドは、以下のように再構築する必要があります。 の情報を使ってオブジェクトを作成します。 はWriteXmlメソッドによって書き込まれました。

このメソッドが呼ばれたとき、リーダー の開始位置に配置される。 の情報を包んでいる要素です。 を使用します。つまり 開始タグ を、シリアライズされたオブジェクトの この メソッドが返されたとき、それは 要素の最初から最後までの全体 そのすべての内容を含む。とは異なり フレームワークは、WriteXml メソッド は、ラッパー要素 を自動生成します。あなたの実装 が必要です。これらの の位置決めルールは、コードが 予期せぬ実行時例外の発生 またはデータが破損しています。

少し不明瞭な点には同意しますが、要するに "するのがあなたの仕事だということです。 Read() ラッパー"の終了要素タグです。