2009-12-30 19 views
7

Estoy tratando de serializar una clase que hereda de una clase base que implementa IXmlSerializable.¿Cómo volver a la serialización XML 'predeterminada' al implementar IXmlSerializable en una clase base?

La clase base, llamada PropertyBag es una clase que permite propiedades dinámicas (credits to Marc Gravell).

Implementé IXmlSerializable para que las propiedades dinámicas (almacenadas en un Diccionario) se escriban como elementos xml normales.

p. Ej. Cuando seria una persona de clase con una propiedad pública Nombre (no dinámico) y una Edad propiedad dinámica, me gustaría para que se genere el siguiente código XML:

<Person> 
    <Name>Tim</Name> 
    <DynamicProperties> 
    <Country> 
     <string>USA</string> 
    </Country> 
    </DynamicProperties> 
<Person> 

puedo conseguir la pieza a trabajar con la siguiente implementación de WriteXml en la clase PropertyBag Base:

public void WriteXml(System.Xml.XmlWriter writer) 
    { 
     writer.WriteStartElement("DynamicProperties"); 

     // serialize every dynamic property and add it to the parent writer 
     foreach (KeyValuePair<string, object> kvp in properties) 
     { 
      writer.WriteStartElement(kvp.Key); 

      StringBuilder itemXml = new StringBuilder(); 
      using (XmlWriter itemWriter = XmlWriter.Create(itemXml)) 
      { 
       // serialize the item 
       XmlSerializer xmlSer = new XmlSerializer(kvp.Value.GetType()); 
       xmlSer.Serialize(itemWriter, kvp.Value);      

       // read in the serialized xml 
       XmlDocument doc = new XmlDocument(); 
       doc.LoadXml(itemXml.ToString()); 

       // write to modified content to the parent writer 
       writer.WriteRaw(doc.DocumentElement.OuterXml); 
      } 

      writer.WriteEndElement(); 
     } 

     writer.WriteEndElement(); 
    } 

sin embargo, cuando la serialización de la clase persona, ya no serializa los normales (no dinámico) propiedades menos que sobrescribir el método WriteXml en persona (que no quiero hacer) ¿Hay alguna forma de que en la clase base pueda agregar automáticamente las propiedades estáticas? Sé que puedo hacerlo manualmente usando la reflexión, pero me preguntaba si hay alguna funcionalidad incorporada en .Net Framework.

+0

Creo que deberías evitar la palabra "estática", ya que tiene otro significado (muy diferente) ... –

Respuesta

1

Marc, su respuesta en poner los FixedProperties en una colección particular me hizo pensar que en lugar de heredar de PropertyBag, debería crear una propiedad de ese tipo.

Así que creé una clase PropertyBagWrapper de la que mi clase Person hereda y funciona.

[Serializable] 
[TypeDescriptionProvider(typeof(PropertyBagDescriptionProvider))]  
public abstract class PropertyBagWrapper 
{ 
    [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)]  
    public PropertyBag DynamicProperties { get; set; } 

    public object this[string name] 
    { 
     get { return DynamicProperties[name]; } 
     set { DynamicProperties[name] = value; } 
    } 
    protected PropertyBagWrapper() 
    { 
     DynamicProperties = new PropertyBag(this.GetType()); 
    } 
} 

[Serializable]  
public class Person : PropertyBagWrapper 
{ 
    [Browsable(true)] 
    public string Name { get; set; } 
} 

No voy a repetir todo el código para la PropertyBag y las clases personalizadas necesarias para la implementación ICustomTypeDescriptor, se puede encontrar que here.

Moví el atributo TypeDescriptionProvider de la clase PropertyBag a la clase PropertyBagWrapper.

La clase PropertyBag todavía tiene la misma implementación para el método WriteXml() tal como se publicó en la pregunta.

3

He pasado bastante tiempo con XmlSerializer (y varias otras API de serialización), y estoy bastante seguro de que simplemente: no se puede. Implementar IXmlSerializable es todo o nada.

Lo más cerca que puedo pensar es hacer trampa y mover todas las propiedades fijas a un subobjeto; esto le daría xml ligeramente diferente - algo como:

<FixedProperties> 
    <Name>Tim</Name> 
</FixedProperties> 
<DynamicProperties> 
    <Country> 
    <string>USA</string> 
    </Country> 
</DynamicProperties> 

pero espero que funcione. Tendría propiedades de paso en su objeto base:

[Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] 
public FixedProperties FixedProps {get;set;} 
public string Name { 
    get {return FixedProps.Name;} 
    set {FixedProps.Name = value;} 
} 

¿Tiene sentido? También puede marcar Name como [XmlIgnore], pero parece bastante redundante. En el método de serialización a medida que tendría que utilizar new XmlSerializer(typeof(FixedProperties))

Editar: Aquí hay un "serializar" ejemplo de trabajo:

using System; 
using System.ComponentModel; 
using System.Xml.Serialization; 

static class Program 
{ 
    static void Main() 
    { 
     MyType obj = new MyType { Name = "Fred" }; 
     var ser = new XmlSerializer(obj.GetType()); 
     ser.Serialize(Console.Out, obj); 
    } 
} 
public class MyType : IXmlSerializable 
{ 
    public MyType() 
    { 
     FixedProperties = new MyTypeFixedProperties(); 
    } 
    [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] 
    public MyTypeFixedProperties FixedProperties { get; set; } 
    [XmlIgnore] 
    public string Name 
    { 
     get { return FixedProperties.Name; } 
     set { FixedProperties.Name = value; } 
    } 

    System.Xml.Schema.XmlSchema IXmlSerializable.GetSchema() 
    { 
     return null; 
    } 

    void IXmlSerializable.ReadXml(System.Xml.XmlReader reader) 
    { 
     throw new System.NotImplementedException(); 
    } 

    void IXmlSerializable.WriteXml(System.Xml.XmlWriter writer) 
    { 
     writer.WriteStartElement("DynamicProperties"); 
     writer.WriteElementString("Foo", "Bar"); 
     writer.WriteEndElement(); 
     fixedPropsSerializer.Serialize(writer, FixedProperties); 
    } 
    static readonly XmlSerializer fixedPropsSerializer 
     = new XmlSerializer(typeof(MyTypeFixedProperties)); 

} 
[XmlRoot("FixedProperties")] 
public class MyTypeFixedProperties 
{ 
    public string Name { get; set; } 
} 
+0

Justo en el dinero. Es por eso que las cosas de DataContract se agregaron en las versiones más nuevas de .Net. Esta es la razón por la que los servicios web "viejos" de asmx también funcionan, porque no puede detectar la serialización de sus objetos sin escribir completamente la serialización desde cero. – jvenema

+0

Eso debería funcionar, pero no para mi propósito previsto. Las clases derivadas ya existen, solo quería agregarles funcionalidad para las propiedades dinámicas. Las propiedades fijas deben permanecer en la clase derivada. ¡Aunque aprecio mucho la entrada! – Timmel

+0

@jvenema - ¿Sería esto posible usar los atributos de DataContract entonces y si es así, tiene algún buen puntero para mí? – Timmel

Cuestiones relacionadas