159

プログラマーが を実装することを決定したら、IXmlSerializableそれを実装するためのルールとベスト プラクティスは何ですか? GetSchema()戻る必要がnullあり、戻るReadXml前に次の要素に移動する必要があると聞きました。これは本当ですか?また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>
4

5 に答える 5

106

はい、GetSchema()はnullを返す必要があります

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

読み取りと書き込みの両方で、オブジェクト要素はすでに書き込まれているため、書き込みで外部要素を追加する必要はありません。たとえば、2つの属性の読み取り/書き込みを開始できます。

書き込み用:

提供するWriteXml実装は、オブジェクトのXML表現を書き出す必要があります。フレームワークはラッパー要素を書き込み、開始後にXMLライターを配置します。実装は、子要素を含むその内容を書き込む場合があります。次に、フレームワークはラッパー要素を閉じます。

そして読むために:

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

このメソッドが呼び出されると、リーダーは、タイプの情報をラップする要素の先頭に配置されます。つまり、シリアル化されたオブジェクトの開始を示す開始タグの直前です。このメソッドが戻るとき、そのすべての内容を含めて、要素全体を最初から最後まで読み取っていなければなりません。WriteXmlメソッドとは異なり、フレームワークはラッパー要素を自動的に処理しません。実装はそうしなければなりません。これらのポジショニングルールに従わないと、コードで予期しないランタイム例外が生成されたり、データが破損したりする可能性があります。

それは少し不明確であることに同意しますが、それは「Read()ラッパーの終了要素タグに対するあなたの仕事です」に要約されます。

于 2008-11-11T05:14:50.880 に答える
35

MSDNのドキュメントは今ではかなり不明確であり、Webで見つけることができる例はほとんどの場合誤って実装されているため、サンプルを使用してこのテーマに関する1つの記事を書きました。

落とし穴は、Marc Gravellがすでに述べたもののほかに、ロケールと空の要素の処理です。

http://www.codeproject.com/KB/XML/ImplementIXmlSerializable.aspx

于 2009-10-27T09:36:57.087 に答える
9

はい、全体が地雷原のようなものですね。Marc Gravellの回答はそれをほぼカバーしていますが、私が取り組んだプロジェクトで、外側の XML 要素を手動で記述しなければならないのは非常に厄介であることがわかったことを追加したいと思います。また、同じタイプのオブジェクトの XML 要素名に一貫性がありませんでした。

IXmlSerializable私たちの解決策は、システムのものから派生した独自のインターフェースを定義することでした。これは、 というメソッドを追加しましたWriteOuterXml()。ご想像のとおり、このメソッドは単純に外側の要素を書き込み、次に を呼び出しWriteXml()、要素の末尾を書き込みます。もちろん、システム XML シリアライザーはこのメソッドを呼び出さないため、独自のシリアライゼーションを行った場合にのみ有用でした。同様に、ReadContentXml()外側の要素を読み取らず、その内容のみを読み取るメソッドを追加しました。

于 2010-04-19T03:48:18.823 に答える
2

クラスの XmlDocument 表現が既にある場合、または XML 構造を操作する XmlDocument の方法を好む場合、IXmlSerializable を実装する手っ取り早い方法は、この xmldoc をさまざまな関数に渡すことです。

警告: XmlDocument (および/または XDocument) は xmlreader/writer よりも桁違いに遅いため、パフォーマンスが絶対的な要件である場合、このソリューションは適していません!

class ExampleBaseClass : IXmlSerializable { 
    public XmlDocument xmlDocument { get; set; }
    public XmlSchema GetSchema()
    {
        return null;
    }
    public void ReadXml(XmlReader reader)
    {
        xmlDocument.Load(reader);
    }

    public void WriteXml(XmlWriter writer)
    {
        xmlDocument.WriteTo(writer);
    }
}
于 2014-04-02T08:56:43.393 に答える