2009-03-10 17 views
9

.NETでのXMLシリアル化では、コンストラクタのextraTypes[]パラメータを使用して多型オブジェクトを使用できます。 IXmlSerializableを実装するタイプのXMLシリアル化のカスタマイズも可能です。多態型とIXmlSerializable

しかし、私はこれらの2つの機能を組み合わせることができないんだ - この最小限の例で示されているように:

型CsFoo.CustomSerializable:

using System; 
using System.IO; 
using System.Xml; 
using System.Xml.Schema; 
using System.Xml.Serialization; 

namespace CsFoo 
{ 
    public class CustomSerializable : IXmlSerializable 
    { 
     public XmlSchema GetSchema() { return null; } 
     public void ReadXml(XmlReader xr) { } 
     public void WriteXml(XmlWriter xw) { } 
    } 

    class CsFoo 
    { 
     static void Main() 
     { 
      XmlSerializer xs = new XmlSerializer(
       typeof(object), 
       new Type[] { typeof(CustomSerializable) }); 

     xs.Serialize(new StringWriter(), new CustomSerializable()); 
    } 
} 

最後の行は、このメッセージでSystem.InvalidOperationExceptionをスローしますこの文脈で パラメータとしてCsFoo.CustomSerializableを使用しないでください。 タイプ、またはクラスまたは構造体のメンバ、パラメータ、戻り値 タイプ、またはメンバをtyとして宣言する必要がありますpe CsFoo.CustomSerializable (これはオブジェクトではありません)。タイプCsFoo.CustomSerializable のオブジェクトは、ArrayListsなどの型指定されていないコレクションでは使用できません。動的に生成されたXMLアセンブリを通じてワタリ

、我々は最終的に呼び出すことにより、バック.NET標準ライブラリのコードに来る:

protected Exception CreateUnknownTypeException(Type type) 
{ 
    if (typeof(IXmlSerializable).IsAssignableFrom(type)) 
    { 
     return new InvalidOperationException(
      Res.GetString("XmlInvalidSerializable", 
      new object[] { type.FullName })); 
    } 

    // Rest omitted... 

リフレクターショー:

System.Xml.Serialization.XmlSerializationWriter.WriteTypedPrimitive(
    String, String, Object, Boolean) : Void 

ターンでは、これはにつながりますリソースXmlInvalidSerializableは上記の文字列に対応しています。つまり、WriteTypedPrimitiveIXmlSerializableが好きではありません。

我々は非多型シリアライザを生成する場合、そのように:これは正しくIXmlSerializableを扱う

System.Xml.Serialization.XmlSerializationWriter.WriteSerializable(
    IXmlSerializable, String, String, Boolean) : Void 

XmlSerializer xs = new XmlSerializer(typeof(CustomSerializable)); 

.NETはへの呼び出しを生成します。 .NETが多型の場合にこの関数を使用しない理由を誰かが知っていますか? XMLシリアライザが生成するC#を見ると、これは非常に簡単に行うことができます。ここで私はテストされていない溶液を用いて、XMLシリアライザから得たいくつかのコードです:

void Write1_Object(string n, string ns, global::System.Object o, 
    bool isNullable, bool needType) 
{ 
    if ((object)o == null) 
    { 
     if (isNullable) WriteNullTagLiteral(n, ns); 
     return; 
    } 
    if (!needType) 
    { 
     System.Type t = o.GetType(); 
     if (t == typeof(global::System.Object)) 
     { 
     } 
>>> patch begin <<< 
+   else if (typeof(IXmlSerializable).IsAssignableFrom(t)) 
+   { 
+    WriteSerializable((System.Xml.Serialization.IXmlSerializable) 
        ((global::CsFoo.CustomSerializable)o), 
+     @"CustomSerializable", @"", true, true); 
+   } 
>>> patch end <<< 
     else 
     { 
      WriteTypedPrimitive(n, ns, o, true); 
      return; 
     } 
    } 
    WriteStartElement(n, ns, o, false, null); 
    WriteEndElement(o); 
} 

これは技術的な理由からか、単に機能の制限のために残されていますか?サポートされていない機能、または私のばかげた?私のintertubes Googleのスキルは私に失敗します。

ここで関連する質問がありました。「C# Xml-Serializing a derived class using IXmlSerializable」が最も関連性があります。それは私がそれが単に不可能であると信じさせる。

この場合、私の現在の考えは、ルートの基本クラスにデフォルトのIXmlSerializable実装を注入することです。そして、すべてがIXmlSerializableになり、.NETは文句を言うことはありません。私はRef @ ve.Emitを使用して、具体的な型ごとにReadXmlWriteXmlのボディを拾い上げて、ライブラリ1を使った場合と同じように見えるXMLを生成することができます。

XMLシリアル化の問題に直面すると、「わかっていますが、Reflection.Emitを使用してコードを生成します」と思う人もいます。今、彼らには2つの問題があります。


P.S.注意;私は.NETのXMLシリアル化の代替案を認識しており、制限があることを知っています。私はまた、POCOを保存することは、抽象的なデータ型を扱うよりもはるかに簡単であることを知っています。しかし、私は古いコードがたくさんあり、既存のXMLスキーマのサポートが必要です。

私はこれがSomeOtherXMLYAMLXAMLProtocolBuffersDataContractRandomJsonLibraryThrift、またはあなたのMorseCodeBasedSerializeToMp3ライブラリ内がいかに簡単であるかを示す応答を感謝しながら、そう - 私は何を期待してることで、 - ちょっと私は何かを学ぶかもしれませんXMLシリアライザの回避策(解決策ではない場合)

+0

とにかく、かなり遅れていますが、IXmlSerializableの場合は余分な種類のソリューションが私のために機能しません。しかし、[このアプローチ](http://www.softwarerockstar.com/2006/12/using-ixmlserializable-to-overcome-not-expected-error-on-derived-classes/)は、デシリアライズされたクラスプロパティクラス(!)とは異なるため、作成する正しい型を見つけるカスタムロジックをそのように実装できます。 – Vlad

+0

もう一つの解決策があります(ここではhttps://connect.microsoft.com/VisualStudio/feedback/details/422577/incorrect-deserialization-of-polymorphic-type-that-implements-ixmlserializable)( 'XmlSchemaProvider'属性を使って) .net型にxml型をバインドする)が、コードが不足していて、私はその考え方を働かせることができませんでした。誰でも? – Vlad

答えて

2

私はobjectを使用した場合、あなたの問題を再現することができました:

XmlSerializer xs = new XmlSerializer(
    typeof(object), 
    new Type[] { typeof(CustomSerializable) }); 

をしかし、私はその後、CustomSerializableの派生クラスを作成しました:

public class CustomSerializableDerived : CustomSerializable 
{ 
} 

をそして、それをシリアル化しようとした:

XmlSerializer xs = new XmlSerializer(
    typeof(CustomSerializable), 
    new Type[] { typeof(CustomSerializableDerived) }); 

これは機能しました。

したがって、この問題は、シリアル化する型として "object"を指定する場合に限られていますが、具体的な基本型を指定した場合は問題にはならないようです。

私は午前中これについてさらに調査をします。

0

IxmlSerializableが機能するためには、クラスにはnullコンストラクタが必要です。

  • MyBaseXmlClass基本クラスを考えてみましょう:IXmlSerializable
    - のGetSchema
  • を実装
  • MyXmlClass:MyBaseXmlClass
    - ReadXmlの説明
    • でWriteXml
+0

-1:Hisにはデフォルトのコンストラクタがあります。あなたのポイントを説明するために、テキストの代わりに実際のコードを使用することができます。 –

1

すべてのポスターステートメントの最初の

は、.NETでのXMLシリアル化は、XmlSerializerをコンストラクタのextraTypes []パラメータを使用して多型のオブジェクトを可能にします。

は間違っています。 MSDN extratypesによればために使用される:又はフィールド場合

配列を返し、extraTypesパラメータが配列に挿入することができるオブジェクトを指定します。

シリアル化されたオブジェクトグラフの多型オブジェクトのどこかが配列を介して返された場合、それらを処理できるということです。

多相型をルートXMLオブジェクトとしてシリアル化する方法を実際には見つけられませんでしたが、標準XMLシリアライザまたはIXmlSerializableのいずれかを使用してオブジェクトグラフにある多型をシリアル化できました。私の解決策については、以下を参照してください:

using System.IO; 
using System.Xml; 
using System.Xml.Schema; 
using System.Xml.Serialization; 

namespace CsFoo 
{ 
    public class Foo 
    { 
    [XmlElement("BarStandard", typeof(BarStandardSerializable))] 
    [XmlElement("BarCustom", typeof(BarCustomSerializable))] 
    public Bar BarProperty { get; set; } 
    } 

    public abstract class Bar 
    { } 

    public class BarStandardSerializable : Bar 
    { } 

    public class BarCustomSerializable : Bar, IXmlSerializable 
    { 
    public XmlSchema GetSchema() { return null; } 
    public void ReadXml(XmlReader xr) { } 
    public void WriteXml(XmlWriter xw) { } 
    } 

    class CsFoo 
    { 
    static void Main() 
    { 
     StringWriter sw = new StringWriter(); 
     Foo f1 = new Foo() { BarProperty = new BarCustomSerializable() }; 
     XmlSerializer xs = new XmlSerializer(typeof(Foo)); 

     xs.Serialize(sw, f1); 
     StringReader sr= new StringReader(sw.ToString()); 
     Foo f2 = (Foo)xs.Deserialize(sr); 
    } 
    } 
} 

注意、定義されたのXmlElementなし

XmlSerializer xs = new XmlSerializer(typeof(Foo), 
      new Type[] { typeof(BarStandardSerializable), 
      typeof(BarCustomSerializable)}); 

または

[XmlInclude(typeof(BarCustomSerializable))] 
[XmlInclude(typeof(BarStandardSerializable))] 
public abstract class Bar 
{ } 

のいずれかを使用すると、コードがシリアライズに失敗する原因となること。

+0

将来の読者への注意:もし 'BarProperty'が' [XmlElement(/ * ... * /)]の代わりに '[XmlArrayItem(/ * ... * /)]' –