2013-06-21 28 views
13

Chúng tôi đang sử dụng XmlSerializer và tôi muốn cung cấp tùy chỉnh tuần tự hóa cho các lớp nhất định. Tuy nhiên, tôi không phải lúc nào cũng có khả năng sửa đổi mã nguồn của lớp đang được đề cập đến, nếu không tôi chỉ có thể làm cho nó thực hiện IXmlSerializable. Có cách nào để làm điều này không?Tôi có thể cung cấp tuần tự hóa tùy chỉnh cho XmlSerializer mà không cần triển khai IXmlSerializable không?

+0

Tôi biết bạn có thể tô điểm cho các lớp học với XmlAttributes khác nhau (như 'XmlElement',' XmlArray', vv), nhưng bạn đang yêu cầu cụ thể về làm thế nào để kiểm soát đầu ra XML của các lớp học không thể thay đổi được? –

+1

Tôi sử dụng các lớp "proxy" mà implined IXmlSerializable với tên thẻ gốc đã cho và sử dụng hàm ReadXml để xây dựng giá trị cơ bản thực, sau đó được hiển thị trong thuộc tính công khai. – asawyer

+0

Cũng có [quá tải hàm dựng] này (http://msdn.microsoft.com/en-us/library/65k4wece.aspx) cho phép bạn ghi đè các thuộc tính được sử dụng trên các loại, nhưng tôi không biết nếu nó cho phép bạn thay đổi hành vi/kết quả của 'IXmlSerializable.ReadXml'. EDIT: Bạn có thể tách mối quan tâm serialization giữa các đối tượng kinh doanh của bạn và các đối tượng bên thứ 3? –

Trả lời

12

Dưới đây là một ví dụ đơn giản của helper Proxy deserialize:

Cho một loại mà chúng ta không thể trực tiếp kiểm soát serialization của ở cấp lớp:

public sealed class Class //contrived example 
{ 
    public string Property {get;set;} 
} 

Và xml chúng ta cần phải deserialize với:

<Class> 
    <Property>Value</Property> 
</Class> 

Bạn có thể tạo loại proxy để xử lý thủ công quá trình deserialization của loại mục tiêu như vậy:

[XmlRoot("Class")] // <-- Very important 
public sealed class ClassSerializerProxy : IXmlSerializable 
{ 
    public Class ClassValue {get;set;} 

    public System.Xml.Schema.XmlSchema GetSchema(){return null;} 
    public void WriteXml(System.Xml.XmlWriter writer){} 

    public void ReadXml(System.Xml.XmlReader reader) 
    { 
     var x = XElement.ReadFrom(reader) as XElement; 
     this.ClassValue = new Class(); 
     //again this is a simple contrived example 
     this.ClassValue.Property = x.XPathSelectElement("Property").Value; 
    } 
} 

Cách sử dụng:

void Main() 
{ 
    // get the xml value somehow 
    var xdoc= XDocument.Parse(@"<Class><Property>Value</Property></Class>"); 

    // deserialize the xml into the proxy type 
    var proxy = Deserialize<ClassSerializerProxy>(xdoc); 

    // read the resulting value 
    var value = proxy.ClassValue; 
} 

public object Deserialize(XDocument xmlDocument, Type DeserializeToType) 
{ 
    XmlSerializer xmlSerializer = new XmlSerializer(DeserializeToType); 
    using (XmlReader reader = xmlDocument.CreateReader()) 
     return xmlSerializer.Deserialize(reader); 
} 

Bây giờ ném trong một số thuốc generic và một phương pháp mở rộng, và chúng tôi có thể làm sạch các trang web gọi lên một chút cho một (TRỪ EXCEPTION XỬ LÝ) phiên bản cuối cùng:

Cách sử dụng:

void Main() 
{ 
    var xml = @"<Class><Property>Value</Property></Class>"; 

    var value = xml.DeserializeWithProxy<ClassSerializerProxy,Class>(); 

    value.Dump(); 
} 

loại dụ của bạn:

public sealed class Class 
{ 
    public string Property {get;set;} 
} 

Một giao diện mà loại proxy phải thực hiện

public interface ISerializerProxy<TInstanceType> where TInstanceType : class 
{ 
    TInstanceType Value { get; } 
} 

Ví dụ proxy bây giờ thực hiện các giao diện mới

[XmlRoot("Class")] 
public sealed class ClassSerializerProxy : IXmlSerializable, ISerializerProxy<Class> 
{ 
    public Class Value {get;set;} 

    public System.Xml.Schema.XmlSchema GetSchema(){return null;} 
    public void WriteXml(System.Xml.XmlWriter writer){} 

    public void ReadXml(System.Xml.XmlReader reader) 
    { 
     var x = XElement.ReadFrom(reader) as XElement; 
     this.Value = new Class(); 
     this.Value.Property = x.XPathSelectElement("Property").Value; 
    } 
} 

Phương pháp deserialization hiện nay là một phương pháp khuyến nông trên string và có thể được sử dụng với bất kỳ proxy kiểu.

public static class ExtensionMethods 
{ 
    public static TInstanceType DeserializeWithProxy<TProxyType,TInstanceType>(this string xml) 
     where TProxyType : ISerializerProxy<TInstanceType> 
     where TInstanceType : class 
    { 
     using (XmlReader reader = XDocument.Parse(xml).CreateReader()) 
     { 
      var xmlSerializer = new XmlSerializer(typeof(TProxyType)); 
      return (xmlSerializer.Deserialize(reader) as ISerializerProxy<TInstanceType>).Value; 
     } 
    } 
} 
+9

Có cách nào để sử dụng phương pháp này nếu tôi có một cây đối tượng lớn, và muốn sử dụng serialization mặc định cho toàn bộ cây, nhưng serialization tùy chỉnh cho một số lá nhất định? Nếu tôi có quyền truy cập vào nguồn, tôi có thể làm cho những lá đó thực hiện IXmlSerializable, và đặt serialization tùy chỉnh ở đó. Nhưng nếu tôi không thể sửa đổi lá, tôi có cần viết mã tuần tự cho toàn bộ cây không? Hay tôi chỉ có thể nhắm vào tập con nhỏ của các lớp bài toán bằng cách nào đó? – Rob

+1

@asawyer Đây là một câu trả lời tuyệt vời! Tôi đã chọn cũng thực hiện chức năng WriteXml vì lớp thực sự của tôi không phải là XML có thể tuần tự hóa được. Nó bao gồm một loạt các accessors chỉ đọc mà chỉ có thể được thiết lập thông qua constructor. –

+0

@natephette Tôi rất vui vì tôi đã có thể trợ giúp! – asawyer