コードを簡素化してスペースを節約しますが、ここで紹介する内容はコアの問題を示しています。
基本型のプロパティを持つクラスがあります。そのプロパティに割り当てることができる3つの派生クラスがあります。
派生クラスのいずれかをコンテナに割り当ててコンテナをシリアル化しようとすると、XmlSerializerは恐ろしいものをスローします。
「タイプxは予期されていませんでした。XmlIncludeまたはSoapInclude属性を使用して、静的に認識されていないタイプを指定します。」
ただし、私の基本クラスはすでにその属性で装飾されているため、追加の「隠された」要件が必要であると考えています。
本当に奇妙な部分は、デフォルトのWCFシリアライザーにこのクラス階層に関する問題がないことです。
コンテナクラス
[DataContract]
[XmlRoot(ElementName = "TRANSACTION", Namespace = Constants.Namespace)]
public class PaymentSummaryRequest : CommandRequest
{
[DataMember]
public PaymentSummary Summary { get; set; }
public PaymentSummaryRequest()
{
Mechanism = CommandMechanism.PaymentSummary;
}
}
基本クラス
[DataContract]
[XmlInclude(typeof(xPaymentSummary))]
[XmlInclude(typeof(yPaymentSummary))]
[XmlInclude(typeof(zPaymentSummary))]
[KnownType(typeof(xPaymentSummary))]
[KnownType(typeof(yPaymentSummary))]
[KnownType(typeof(zPaymentSummary))]
public abstract class PaymentSummary
{
}
派生クラスの1つ
[DataContract]
public class xPaymentSummary : PaymentSummary
{
}
シリアル化コード
var serializer = new XmlSerializer(typeof(PaymentSummaryRequest));
serializer.Serialize(Console.Out,new PaymentSummaryRequest{Summary = new xPaymentSummary{}});
例外
System.InvalidOperationException:XMLドキュメントの生成中にエラーが発生しました。 ---> System.InvalidOperationException:タイプxPaymentSummaryは予期されていませんでした。 XmlIncludeまたはSoapInclude属性を使用して、静的に認識されないタイプを指定します。で
Microsoft.Xml.Serialization.GeneratedAssembly.XmlSerializationWriterPaymentSummaryRequest.Write13_PaymentSummary(String n、String ns、PaymentSummary o、Boolean isNullable、Boolean needType)で
Microsoft.Xml.Serialization.GeneratedAssembly.XmlSerializationWriterPaymentSummaryRequest.Write14_PaymentSummaryRequest(String n、String ns、PaymentSummaryRequest o、Boolean isNullable、Boolean needType)at
Microsoft.Xml.Serialization.GeneratedAssembly.XmlSerializationWriterPaymentSummaryRequest.Write15_TRANSACTION(Object o)---内部例外スタックトレースの終了--- at
System.Xml.Serialization.XmlSerializer.Serialize(XmlWriter xmlWriter、Object o、XmlSerializerNamespaces namespaces、String encodingStyle、String id)で
System.Xml.Serialization.XmlSerializer.Serialize(TextWriter textWriter、Object o、XmlSerializerNamespaces namespaces)
c:\ Users\Tedford\AppData\Local\Temp\uqacncyo.0.cs:line 47のUserQuery.RunUserAuthoredQuery()で
あなたが見ている問題は、PaymentSummaryRequest
が名前空間を設定しているためです。名前空間を削除するか、PaymentSummary
クラスに名前空間を追加できます。
[XmlRoot(Namespace = Constants.Namespace)]
[XmlInclude(typeof(xxxPaymentSummary))]
public abstract class PaymentSummary
{
}
@Tedfordが以下のコメントで言及しているように、名前空間は派生型を使用する場合にのみ必要です。
XMLシリアル化アセンブリを生成するとき、ルートノードには名前空間が設定され、基本クラスにはないため、生成されたシリアル化アセンブリにXMLインクルードロジックが含まれていないようです。
この問題を解決する別のアプローチは、クラス自体から名前空間宣言を削除し、XmlSerializer
コンストラクターで名前空間を指定することです。
var serializer = new XmlSerializer(
typeof(PaymentSummaryRequest),
Constants.Namespace
);
私は同じ問題を抱えており、いくつかのグーグルが私をここに導いた。基本クラスの実装ごとにXMLInclude
属性を持つことを受け入れることができませんでした。一般的なシリアル化方法で動作するようにしました。まず、OPの元の例の一部ですが、明確にするために少し変更しました。
public abstract class PaymentSummary
{
public string _summary;
public string _type;
}
public class xPaymentSummary : PaymentSummary
{
public xPaymentSummary() { }
public xPaymentSummary(string summary)
{
_summary = summary;
_type = this.GetType().ToString();
}
}
上記に加えて、yPaymentSummary
とzPaymentSummary
もまったく同じ実装で存在します。これらはコレクションに追加され、それぞれでシリアル化メソッドを呼び出すことができます。
List<PaymentSummary> summaries = new List<PaymentSummary>();
summaries.Add(new xPaymentSummary("My summary is X."));
summaries.Add(new yPaymentSummary("My summary is Y."));
summaries.Add(new zPaymentSummary("My summary is Z."));
foreach (PaymentSummary sum in summaries)
SerializeRecord(sum);
最後に、シリアル化メソッド-シリアル化時にレコードのタイプを使用する単純な汎用メソッド:
static void SerializeRecord<T>(T record) where T: PaymentSummary
{
var serializer = new XmlSerializer(record.GetType());
serializer.Serialize(Console.Out, record);
Console.WriteLine(" ");
Console.WriteLine(" ");
}
上記の結果、次の出力が生成されます。