2013-06-21 30 views
13

Używamy XmlSerializer i chcę zapewnić niestandardową serializację dla niektórych klas. Jednak nie zawsze mam możliwość modyfikowania kodu źródłowego danej klasy, w przeciwnym razie mógłbym po prostu dokonać implementacji IXmlSerializable. Czy jest jakiś sposób to zrobić?Czy mogę zapewnić niestandardową serializację dla XmlSerializer bez implementacji IXmlSerializable?

+0

wiem można zdobić zajęcia z różnych XmlAttributes (jak '' XmlArray' XmlElement', itp), ale pytasz konkretnie o tym, jak sterować wyjściem XML klas niemodyfikowalne? –

+1

Używam klas "proxy", które implikują IXmlSerializable z podaną nazwą głównego znacznika i używają funkcji ReadXml do konstruowania rzeczywistej wartości bazowej, która jest następnie eksponowana w publicznej własności. – asawyer

+0

Cóż, istnieje przeciążenie konstruktora (http://msdn.microsoft.com/en-us/library/65k4wece.aspx), które pozwala zastąpić atrybuty używane w typach, ale nie wiem, czy to pozwala zmienić zachowanie/wynik 'IXmlSerializable.ReadXml'. EDYCJA: Czy można oddzielić obawy związane z serializacją między obiektami biznesowymi a obiektami stron trzecich? –

Odpowiedz

12

Oto prosty przykład pomocnika deserializowania Proxy:

przypisany typ, że nie możemy bezpośrednio kontrolować serializacji na poziomie klasy:

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

a xml musimy deserializowania z:

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

Można utworzyć typ proxy, aby ręcznie przeprowadzić proces deserializacji typu docelowego:

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

użycia:

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

teraz rzucać w niektórych leków generycznych i metodę rozszerzenia, a my możemy oczyścić witrynie Up Call trochę dla końcowego (z wyjątkiem Obsługa wyjątków) wersji:

Zastosowanie:

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

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

    value.Dump(); 
} 

Twój typ instancji:

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

interfejs że typy proxy musi implementować

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

Przykład Pełnomocnik obecnie wprowadza nowy interfejs

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

Sposób deserializacji jest teraz metodę rozszerzenia na string i może być używany z dowolnym proxy rodzaj.

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

Czy istnieje sposób użycia tego podejścia, jeśli mam duże drzewo obiektów i chcę użyć domyślnej serializacji dla drzewa jako całości, ale niestandardową serializację dla niektórych liści? Jeśli mam dostęp do źródła, mogę sprawić, by te liście zaimplementowały IXmlSerializable i umieściły tam niestandardową serializację. Ale jeśli nie mogę zmodyfikować liści, czy muszę napisać kod serializacji dla całego drzewa? A może w jakiś sposób celuję w mały podzbiór klas problemów? – Rob

+1

@asawyer To jest świetna odpowiedź! Zdecydowałem się również na wdrożenie funkcji WriteXml, ponieważ moja prawdziwa klasa nie jest sama w sobie możliwa do serializowania. Składa się z zestawu akcesorów tylko do odczytu, które można ustawić tylko za pomocą konstruktora. –

+0

@nadephette Jestem bardzo szczęśliwy, że mogłem pomóc! – asawyer