2009-03-10 27 views
9

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 ReadXmlWriteXml 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.

+0

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

+0

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

Trả lời

2

tôi đã có thể để tái tạo vấn đề của bạn, khi sử dụng object:

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

Tuy nhiên, tôi sau đó tạo ra một lớp có nguồn gốc của CustomSerializable:

public class CustomSerializableDerived : CustomSerializable 
{ 
} 

Và cố gắng để serialize nó:

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

Điều này hiệu quả.

Vì vậy, có vẻ như vấn đề bị hạn chế đối với trường hợp bạn chỉ định "đối tượng" làm loại để tuần tự hóa, nhưng không phải nếu bạn chỉ định loại cơ sở cụ thể.

Tôi sẽ thực hiện một số nghiên cứu thêm về điều này vào buổi sáng.

0

Để IxmlSerializable hoạt động, lớp cần phải có hàm tạo null.

Hãy xem xét một lớp cơ sở

  • MyBaseXmlClass: IXmlSerializable
    - thực hiện GetSchema
  • MyXmlClass: MyBaseXmlClass
    - ReadXml
    • WriteXml
+0

-1: Của anh ta có một constructor mặc định; bạn có thể sử dụng mã thực tế thay vì chỉ văn bản để minh họa điểm của bạn. –

1

Trước hết áp phích tuyên bố

Các serialization XML trong .NET cho phép đối tượng đa hình qua extraTypes [] tham số của các nhà xây dựng XmlSerializer.

là gây hiểu lầm. Theo MSDN extratypes được sử dụng cho:

Nếu một hoặc lĩnh vực bất động sản trả một mảng, tham số extraTypes quy định cụ thể đối tượng có thể được chèn vào mảng.

Có nghĩa là nếu một nơi nào đó trong đối tượng được đa dạng hóa đồ thị đối tượng được tuần tự hóa của bạn được trả về qua mảng, chúng có thể được xử lý. Trong khi tôi đã không thực sự tìm thấy một giải pháp làm thế nào để serialize một loại đa hình như đối tượng XML gốc, tôi đã có thể serialize các loại đa hình tìm thấy trong đồ thị đối tượng bằng cách sử dụng hoặc serializer XML tiêu chuẩn hoặc IXmlSerializable. Xem dưới đây để biết giải pháp của tôi:

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); 
    } 
    } 
} 

Lưu ý rằng việc sử dụng một trong hai

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

hoặc

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

mà không XmlElement xác định, sẽ gây ra các mã thất bại tại serialization.

+0

Lưu ý cho người đọc trong tương lai: nếu 'BarProperty' giống như một mảng' [XmlArrayItem (/ * ... * /)] 'phải được sử dụng thay cho' [XmlElement (/ * ... * /)] ' –

Các vấn đề liên quan