2016-12-08 6 views
1

カスタムXMLシリアル化でのみシリアル化できるクラスがあります。これは、使用するはるかに大きなシステム内の特定のクラスのプロパティとして使用されます基本的なシリアル化。私はシステム全体をカスタムシリアル化に変換することはできません。巨大で、将来的には基本的なシリアル化を使用するサードパーティのモジュールが含まれている可能性があるからです。Listがカスタムシリアル化を使用する複数のオブジェクトを参照すると、基本的なXMLシリアル化が失敗する

私の最初の質問は、これがまったく許されるかどうかです。私はMSDNで、あなたが基本シリアル化されたものからカスタムシリアル化されたオブジェクトを参照することを許可されていないと言うことはできませんし、そうでない場合にはコードの移植性に重大な制限があるように見えます。しかし、このサイトの初期の答えは、あなたができないかもしれないことを示唆しているようです(このポスターは「あなたが実際に直列化を混ぜ合わせて一致させることはできません、一度IXmlSerializableを実装すれば、クラス内でのミキシングとマッチングを行うことができます。実際にはこれが許されていると仮定すると、メインシステム内のクラスがカスタムシリアル化を持つクラスの2つ以上のオブジェクトを含むリストを実装するときは常に失敗するということです。

興味深いことに、エラーはデシリアライズ時にのみ発生し、そのようなオブジェクトへの参照が複数存在する場合にのみ発生します。さらに興味深いのは、リストが依存関係のチェーンの上に表示されても、同じ方法で失敗します(たとえば、リストには通常のオブジェクトが含まれています(通常のオブジェクトが含まれます。

私は、以下のように最も単純なケースを示す小さなテストプログラムを書いています。

だから私の質問は以下のとおりです。

  1. は、それが実際に 基本シリアル化されたものから、カスタムシリアル化されたオブジェクトを参照することができますか?
  2. もしそうなら、私は何か愚かなことをしていますか?
  3. これが既知のバグですか?テストプログラムON

NOTES: データクラス構造が非常に簡単であり、(基本的なシリアライゼーションを使用)クラスBasicXml及び(カスタムシリアル化を使用して)クラスCustomXmlから成ります。 BasicXmlには、CustomXmlのリストが含まれています。 もう一方のクラスには自己テストのテストが含まれています。インスタンス化してRunTests()を実行するだけです。

using System; 
using System.Collections.Generic; 
using System.Diagnostics; 
using System.IO; 
using System.Xml; 
using System.Xml.Linq; 
using System.Xml.Schema; 
using System.Xml.Serialization; 

namespace AdHocTests 
{ 
    [Serializable] 
    public class BasicXml 
    { 
     public List<CustomXml> TestList { get; set; } 
    } 
    [Serializable] 
    public class CustomXml : IXmlSerializable 
    { 
     public CustomXml() { } 
     public CustomXml(string name) 
     { 
      Name = name; 
     } 
     public string Name { get; set; } 
     public void ReadXml(XmlReader reader) 
     { 
      Name = reader.ReadString(); 
     } 
     public void WriteXml(XmlWriter writer) 
     { 
      writer.WriteString(Name); 
     } 
     public XmlSchema GetSchema() 
     { 
      return null; // I have removed this code for clarity 
     } 
    } 
    public class MixedSerializationTest 
    { 
     public MixedSerializationTest() 
     { 
      _serializer.UnknownElement += UnknownElementHandler; 
     } 
     public void RunTests() 
     { 
      RunOneTest(makeItFail: false); 
      RunOneTest(makeItFail: true); 
     } 
     private void RunOneTest(bool makeItFail) 
     { 
      Debug.Write("\n\nRUNNING TEST THAT WILL " + (makeItFail ? "FAIL" : "PASS") + ":\n\n"); 
      CustomXml c1 = new CustomXml("Hello"); 
      CustomXml c2 = new CustomXml("World"); 
      BasicXml b1 = new BasicXml 
      { 
       TestList = makeItFail ? new List<CustomXml> { c1, c2 } : new List<CustomXml> { c1 } 
      }; 
      XElement xml1 = GetXmlFromObject(b1); 
      Debug.Write("Serialized XML:\n" + xml1.ToString() + "\n=====\n"); 
      BasicXml b2 = GetObjectFromXml(xml1); 
      if (_cancelDeserialization) return; 
      XElement xml2 = GetXmlFromObject(b2); 
      Debug.Write("Reserialized XML:\n" + xml2.ToString() + "\n=====\n"); 
     } 
     private XElement GetXmlFromObject(BasicXml obj) 
     { 
      using (StringWriter sw = new StringWriter()) 
      { 
       using (XmlWriter xw = XmlWriter.Create(sw)) 
       { 
        _serializer.Serialize(xw, obj); 
        return XElement.Parse(sw.ToString()); 
       } 
      } 
     } 
     private BasicXml GetObjectFromXml(XElement xml) 
     { 
      using (StringReader sr = new StringReader(xml.ToString())) 
      { 
       XmlWriterSettings settings = new XmlWriterSettings(); 
       using (XmlReader xr = XmlReader.Create(sr)) 
       { 
        return (BasicXml)_serializer.Deserialize(xr); 
       } 
      } 
     } 
     private void UnknownElementHandler(object sender, XmlElementEventArgs e) 
     { 
      Debug.Write("\n*** Serializer threw an UnknownElement exception ***\n\n"); 
      _cancelDeserialization = true; 
     } 
     private XmlSerializer _serializer = new XmlSerializer(typeof(BasicXml)); 
     private bool _cancelDeserialization = false; 
    } 
} 
+0

CustomXmlが* IXmlSerializableを実装していない場合は動作しますが、これはいくつか不足していることを示しています。 – RamblinRose

+1

ニース[mcve]!私はすぐに問題を再現することができました。 – dbc

+0

こんにちはRamblinRose、よくIXmlSerializableインターフェイスを削除するのは、カスタムではなく基本的なシリアル化に戻すスイッチなので、通常の基本+基本的な状況に戻っています。そして、インターフェイスが(基本的な+カスタムの状況を示す)定位置にあるとき、それはほぼ常に動作し、常に完全にシリアライズされます。これは、コードが正しいかもしれないことを示唆しています。私が描いた非常に特殊な状況の下では、この奇妙なことをするのです。 – Geoffff

答えて

1

あなたは確かにXmlSerializerに渡されたいくつかの大きなオブジェクトグラフに含まれるIXmlSerializable型のインスタンスを含めることができます。

問題は次のとおりです。あなたのReadXml()方法では、あなたはXmlReader.ReadString()の代わりにXmlReader.ReadElementContentAsString()を呼び出す必要があります:

public void ReadXml(XmlReader reader) 
{ 
    Name = reader.ReadElementContentAsString(); 
} 

としてはthis answerで説明:

ReadXmlの説明の方法は、WriteXmlメソッドによって書かれた情報を使用してオブジェクトを再構築しなければなりません。

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

したがって、</CustomXml>エンドエレメントタグを必ず使用する必要があります。そして、ReadElementContentAsString()ためdocsによると、それはあなたが必要なものだけを行います。

この方法では、開始タグを読み込む要素の内容、および終了要素タグを過ぎてリーダーを移動します。一方

docsReadString()のための状態:

私たちは、あなたが文字列として要素またはテキストノードの内容を読み取るためにReadElementContentAsStringメソッドを使用することをお勧めします。

だから、それから離れてください。

+0

これはすばらしいことですが、dbcのおかげで小さな変更が完全に修正され、「予期しないランタイム例外またはデータが破損していました」という理由が説明されています。そして、非常に明確な説明も、ありがとう。 – Geoffff

関連する問題