あなたの問題はdocumentationで説明したように、ReadXml()
はそのラッパー要素だけでなく、その内容を消費しなければならない、ということである:
ReadXml
方法は、によって書かれた情報を使用してオブジェクトを再構築しなければなりませんWriteXml
メソッド。
このメソッドが呼び出されると、リーダーは、タイプの情報をラップする開始タグの上に配置されます。つまり、直列化されたオブジェクトの先頭を示す開始タグの直上にあります。 このメソッドが返ってくると、すべての内容を含め、要素全体を最初から最後まで読み取る必要があります。 WriteXml
メソッドとは異なり、フレームワークはラッパー要素を自動的に処理しません。実装ではそうする必要があります。これらの配置ルールを守らないと、予期しないランタイム例外が発生したり、データが破損する可能性があります。
MyClass.ReadXml()
MyClass
オブジェクトは、ルート要素としてシリアル化されない場合に無限ループを引き起こす、これを実行されません。
public class MyClass : IXmlSerializable
{
public int A { get; set; }
public int B { get; set; }
public XmlSchema GetSchema()
{
return null;
}
public void ReadXml(XmlReader reader)
{
/*
* https://msdn.microsoft.com/en-us/library/system.xml.serialization.ixmlserializable.readxml.aspx
*
* When this method is called, the reader is positioned at the start of the element that wraps the information for your type.
* That is, just before the start tag that indicates the beginning of a serialized object. When this method returns,
* it must have read the entire element from beginning to end, including all of its contents. Unlike the WriteXml method,
* the framework does not handle the wrapper element automatically. Your implementation must do so. Failing to observe these
* positioning rules may cause code to generate unexpected runtime exceptions or corrupt data.
*/
var isEmptyElement = reader.IsEmptyElement;
this.A = XmlConvert.ToInt32(reader.GetAttribute("A"));
this.B = XmlConvert.ToInt32(reader.GetAttribute("B"));
reader.ReadStartElement();
if (!isEmptyElement)
{
reader.ReadEndElement();
}
}
public void WriteXml(XmlWriter writer)
{
writer.WriteAttributeString("A", XmlConvert.ToString(this.A));
writer.WriteAttributeString("B", XmlConvert.ToString(this.B));
}
}
今すぐあなたの<MyClass>
要素が入れ子になったか、オプションの要素を持つ非常に簡単です:代わりに、あなたのMyClass
はこのような何かを見なければなりません。より複雑なカスタムのシリアライゼーションには、ReadXml()
メソッドの正確な読み込みを保証するために採用できる2つの戦略があります。
まず、XNode.ReadFrom()
を呼び出して、現在の要素をXElement
に読み込むことができます。
public class MyClass : IXmlSerializable
{
public int A { get; set; }
public int B { get; set; }
public XmlSchema GetSchema()
{
return null;
}
public void ReadXml(XmlReader reader)
{
var element = (XElement)XNode.ReadFrom(reader);
this.A = (int)element.Attribute("A");
this.B = (int)element.Attribute("B");
}
public void WriteXml(XmlWriter writer)
{
writer.WriteAttributeString("A", XmlConvert.ToString(this.A));
writer.WriteAttributeString("B", XmlConvert.ToString(this.B));
}
}
第二に、あなたが必要なXMLコンテンツが消費されていることを確認するためにXmlReader.ReadSubtree()
を使用することができます:
public class MyClass : IXmlSerializable
{
public int A { get; set; }
public int B { get; set; }
public XmlSchema GetSchema()
{
return null;
}
protected virtual void ReadXmlSubtree(XmlReader reader)
{
this.A = XmlConvert.ToInt32(reader.GetAttribute("A"));
this.B = XmlConvert.ToInt32(reader.GetAttribute("B"));
}
public void ReadXml(XmlReader reader)
{
// Consume all child nodes of the current element using ReadSubtree()
using (var subReader = reader.ReadSubtree())
{
subReader.MoveToContent();
ReadXmlSubtree(subReader);
}
reader.Read(); // Consume the end element itself.
}
public void WriteXml(XmlWriter writer)
{
writer.WriteAttributeString("A", XmlConvert.ToString(this.A));
writer.WriteAttributeString("B", XmlConvert.ToString(this.B));
}
}
これはで動作するように簡単に
XmlReader
から直接解析するよりも少し多くのメモリが必要ですが、
ずっとです
いくつかの最終ノート:
は<MyClass />
と01の両方を処理するようにしてください。これらの2つの形式は意味的に同一であり、送信側のシステムでどちらかを選択できます。
XmlConvert
クラスから、XMLとのプリミティブを変換する方法を推奨します。これにより、国際化が正しく処理されます。
インデントの有無にかかわらずテストしてください。場合によってはReadXml()
メソッドが余分なXMLノードを消費しますが、インデントが有効になっていると、バグは隠されます。
How to Implement IXmlSerializable Correctlyを参照してください。
出典
2017-03-08 17:23:51
dbc