2009-07-02 17 views
3

XmlSerializerを使用してカスタムクラスをデシリアライズしようとしていて、いくつかの問題があります。デシリアライズしようとしている型がわからないこととプラグイン可能ですそれを判断するのが難しい。C#カスタムXmlシリアル化

私はthis postに似ていますが、XmlSerializableのインターフェイスを非直列化する必要があるため、私のアプローチではうまく動作しません。

私が現在入手しているのは、フォームのものです。プラグインを介して実装されるクラスAとクラスBの両方を処理できることを期待し、必要とすることに注意してください。ですから、私がIXmlSerializableを使用することを避けることができれば、それは素晴らしいことでしょう。

ReadXml for Aは、私が立ち往生しているものです。しかし、システムを改善するために私ができる他の変更があるなら、私はそうすることを喜んでします。

public class A : IXmlSerializable 
{ 
    public IB MyB { get; set;} 

    public void ReadXml(System.Xml.XmlReader reader) 
    { 
     // deserialize other member attributes 

     SeekElement(reader, "MyB"); 
     string typeName = reader.GetAttribute("Type"); 

     // Somehow need to the type based on the typename. From potentially 
     //an external assembly. Is it possible to use the extra types passed 
     //into an XMlSerializer Constructor??? 
     Type bType = ??? 

     // Somehow then need to deserialize B's Members 
     // Deserialize X 
     // Deserialize Y 
    } 

    public void WriteXml(System.Xml.XmlWriter writer) 
    { 
     // serialize other members as attributes 

     writer.WriteStartElement("MyB"); 
     writer.WriteAttributeString("Type", this.MyB.GetType().ToString()); 
     this.MyB.WriteXml(writer); 
     writer.WriteEndElement(); 
    } 

    private void SeekElement(XmlReader reader, string elementName) 
    { 
     ReaderToNextNode(reader); 
     while (reader.Name != elementName) 
     { 
     ReaderToNextNode(reader); 
     } 
    } 

    private void ReaderToNextNode(XmlReader reader) 
    { 
     reader.Read(); 
     while (reader.NodeType == XmlNodeType.Whitespace) 
     { 
     reader.Read(); 
     } 
    } 
} 

public interface IB : IXmlSerializable 
{ 
} 

public class B : IB 
{ 

    public void ReadXml(XmlReader reader) 
    { 
     this.X = Convert.ToDouble(reader.GetAttribute("x")); 
     this.Y = Convert.ToDouble(reader.GetAttribute("y")); 
    } 

    public void WriteXml(XmlWriter writer) 
    { 
     writer.WriteAttributeString("x", this.X.ToString()); 
     writer.WriteAttributeString("y", this.Y.ToString()); 
    } 
} 

注:BがインターフェイスIBを使用すると考えられていたように更新されました。少し間違った質問を申し訳ありません。

答えて

4

文字列からインスタンスを作成するには、Activator.CreateInstanceのオーバーロードのいずれかを使用します。その名前の型を取得するには、Type.GetTypeを使用します。

+0

これは興味深いアプローチです。だから、その時点であなたの提案は、その作成された型のReadXml()を呼び出してメンバを初期化しますか?それは、私が型を取得して、おそらく外部の型の1つを私のライブラリに作ることができるかどうかによって機能するかもしれません。 – Ian

+0

私はあなたのコメントに "私は何とか型名に基づいた型が必要です"と答えた。 –

+0

ありがとうジョン。 XMLから型を読み込んでインスタンスを作成することで、作成したインスタンスのReadXmlメソッドを使用してインスタンスを直列化解除することができました。私のインターフェイスでうまく動作します。 – Ian

0

xpathを使用して、入力されたxmlにクラスAまたはクラスBが含まれているかどうかをすぐに把握したい場合は、それに基づいてデシリアライズします。

+0

booo for xpath :( –

+0

heh、hey私はxpathのもっとエキゾチックな機能から遠ざかっていますが...。 –

+0

非常に簡単な例を教えてください。私は実際にxpathを使っていません。探してみる価値があるかどうかは不思議です。 XMLをできるだけ直接処理することを望んでいました。 – Ian

1

私はあなたが実行する前に、実際のタイプを知らないので、あなたが動的にXmlSerializerに属性のオーバーライドを追加することができますIXmlSerializable ...

を実装する必要はないと思います。あなただけが別のクラスのプロパティとしてAを使用する場合、たとえば、Aから継承するタイプのリストを知っておく必要があります。

public class SomeClass 
{ 
    public A SomeProperty { get; set; } 
} 

あなたは動的にそのプロパティにそれぞれの派生型についてXmlElementAttribute Sを適用することができます。

XmlAttributes attr = new XmlAttributes(); 
var candidateTypes = from t in AppDomain.CurrentDomain.GetAssemblies().SelectMany(a => a.GetTypes()) 
        where typeof(A).IsAssignableFrom(t) && !t.IsAbstract 
        select t; 
foreach(Type t in candidateTypes) 
{ 
    attr.XmlElements.Add(new XmlElementAttribute(t.Name, t)); 
} 

XmlAttributeOverrides overrides = new XmlAttributeOverrides(); 
overrides.Add(typeof(SomeClass), "SomeProperty", attr); 

XmlSerializer xs = new XmlSerializer(typeof(SomeClass), overrides); 
... 

これは単なる非常に基本的な例ですが、静的に実行できない場合に実行時にXMLシリアル化属性を適用する方法を示しています。

+0

トーマス、これは私が参照した他の記事のMarksの答えと似ていて、私の考えの解決策だと思う。 問題はA.MyBプロパティにあります(質問を明確にするために更新しました)。これを前もってシリアル化しようとすると、MyBは実際にインターフェイスなのでエラーが発生します。 提案された方法でこのインターフェイスのシリアライズ/デシリアライズを組み合わせる方法はありますか? – Ian

+1

残念ながら、XMLシリアル化はインターフェイスをシリアル化しません。その場合、IXmlSerializableを使用する必要があります。 –

+0

ええ、私は思ったように...私のインタフェースが抽象クラスでなければならないのだろうかと思っています。私はそれがうまくいくと仮定しており、AbstractBを直接的に逆直列化しようとするのではなく、クラスの階層(例えば、AbstractBを実装しているB)を逆シリアル化することになります。 – Ian