Việc tuần tự hóa XML trong .NET cho phép các đối tượng đa hình thông qua tham số extraTypes[]
của hàm tạo XmlSerializer
. Nó cũng cho phép tùy chỉnh tuần tự hóa XML cho các kiểu thực thi IXmlSerializable
.Các loại đa hình và IXmlSerializable
Tuy nhiên, tôi không thể kết hợp hai tính năng này - như đã chứng minh trong ví dụ tối thiểu này:
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());
}
}
Dòng cuối cùng ném System.InvalidOperationException
với tin nhắn này:
Loại CsFoo.CustomSerializable có thể không được sử dụng trong ngữ cảnh này để sử dụng CsFoo.CustomSerializable làm tham số, trả về loại hoặc thành viên của lớp hoặc cấu trúc, tham số, trả về loại hoặc thành viên phải được khai báo là pe CsFoo.CustomSerializable (không thể là đối tượng). Các đối tượng kiểu CsFoo.CustomSerializable có thể không được sử dụng trong các bộ sưu tập không gõ, chẳng hạn như ArrayLists.
Lội qua các hội XML tạo động, chúng tôi cuối cùng trở về với NET mã thư viện tiêu chuẩn bằng cách gọi:
System.Xml.Serialization.XmlSerializationWriter.WriteTypedPrimitive(
String, String, Object, Boolean) : Void
Đổi lại, điều này dẫn đến:
protected Exception CreateUnknownTypeException(Type type)
{
if (typeof(IXmlSerializable).IsAssignableFrom(type))
{
return new InvalidOperationException(
Res.GetString("XmlInvalidSerializable",
new object[] { type.FullName }));
}
// Rest omitted...
Reflector show rằng tài nguyên XmlInvalidSerializable
tương ứng với chuỗi ở trên - ví dụ: WriteTypedPrimitive
không giống như IXmlSerializable
.
Nếu chúng ta tạo ra một serializer không đa hình, như vậy:
XmlSerializer xs = new XmlSerializer(typeof(CustomSerializable));
.NET sẽ tạo ra một cuộc gọi đến:
System.Xml.Serialization.XmlSerializationWriter.WriteSerializable(
IXmlSerializable, String, String, Boolean) : Void
này xử lý IXmlSerializable
đúng cách. Có ai biết tại sao .NET không sử dụng chức năng này trong trường hợp đa hình? Nhìn vào C# mà serializer XML tạo ra, nó xuất hiện với tôi điều này có thể được thực hiện khá dễ dàng. Dưới đây là một số mã tôi nhận được từ bộ nối tiếp XML, với một giải pháp chưa được kiểm tra:
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);
}
Đây có phải là lý do kỹ thuật hay chỉ giới hạn tính năng? Tính năng không được hỗ trợ hoặc thành ngữ của tôi? My intertubes Các kỹ năng của Google không thành công với tôi.
Tôi đã tìm thấy một số câu hỏi có liên quan ở đây, với "C# Xml-Serializing a derived class using IXmlSerializable" là phù hợp nhất. Điều đó khiến tôi tin rằng điều đó không thể thực hiện được.
Trong trường hợp đó, suy nghĩ hiện tại của tôi là tiêm thực thi IXmlSerializable
mặc định trong lớp cơ sở gốc. Sau đó, mọi thứ sẽ là IXmlSerializable
và .NET sẽ không phàn nàn. Tôi có thể sử dụng Reflection.Emit để whip ra các cơ quan ReadXml
và WriteXml
cho từng loại cụ thể, tạo XML trông giống như nếu tôi sử dụng thư viện một.
Một số người, khi đối mặt với một vấn đề serialization XML, hãy nghĩ "Tôi biết, tôi sẽ sử dụng Reflection.Emit để tạo mã". Bây giờ họ có hai vấn đề.
P.S. Chú thích; Tôi nhận thức được các lựa chọn thay thế để tuần tự hóa XML của .NET, và biết nó có những hạn chế. Tôi cũng biết rằng việc tiết kiệm một POCO đơn giản hơn rất nhiều so với việc xử lý các kiểu dữ liệu trừu tượng. Nhưng tôi có một đống mã kế thừa và cần hỗ trợ cho các lược đồ XML hiện có.
Vì vậy, trong khi tôi đánh giá cao trả lời cho thấy cách dễ dàng này là trong SomeOtherXML
, YAML
, XAML
, ProtocolBuffers
, DataContract
, RandomJsonLibrary
, Thrift
, hoặc thư viện MorseCodeBasedSerializeToMp3
của bạn - hey tôi có thể học được điều gì -, những gì tôi hy vọng là một XML serializer làm việc xung quanh, nếu không phải là giải pháp.
Mặc dù một bổ sung khá muộn, dù sao: giải pháp với các loại bổ sung không hoạt động đối với tôi trong trường hợp IXmlSerializable. Tuy nhiên [cách tiếp cận này] (http://www.softwarerockstar.com/2006/12/using-ixmlserializable-to-overcome-not-expected-error-on-derived-classes/) đang hoạt động, vì nó cho phép lớp deserialized khác với lớp thuộc tính (!), vì vậy bất kỳ logic tùy chỉnh nào tìm đúng loại để tạo đều có thể được triển khai theo cách đó. – Vlad
Có một giải pháp khác được khai báo [ở đây] (https://connect.microsoft.com/VisualStudio/feedback/details/422577/incorrect-deserialization-of-polymorphic-type-that-implements-ixmlserializable) (sử dụng thuộc tính 'XmlSchemaProvider' để liên kết các loại xml với các loại .net), nhưng nó thiếu mã, và tôi không thể làm cho ý tưởng đó hoạt động được. Bất kỳ ai? – Vlad