2012-02-29 10 views
6

tengo una propiedad:Cómo serializar propiedad de tipo de objeto con XmlSerializer

public object Tag 

pero puede contener número finito de tipos, lamentablemente sin tipo base (excepto el tipo de objeto). Pero cuando serializo el objeto con esta propiedad, no se serializa. ¿Hay alguna manera de instruir a XmlSerializer con los posibles tipos?

+0

yo esperaría que podría implementar ISerializable y controlar la serialización de la objeto que depende del tipo de etiqueta fundiéndola en su tipo serializable. Sin embargo, no sé cómo deserializarías el objeto más tarde. Posiblemente no sea una buena idea serializar cosas de tipo objeto. –

+1

@Adrian 'XmlSerializer' no se preocupa por' ISerializable'; sin embargo, estoy de acuerdo en que, francamente, este escenario simplemente se evita –

+0

@Marc XmlSerializer no se preocupa por ISerialiable, pero sí se preocupa por IXmlSerializable y llamará a los métodos de lectura y escritura en un objeto que lo implemente. – Fen

Respuesta

9

No recomiendo esto, pero sí, se puede utilizar [XmlElement] etc contarla sobre múltiples tipos de candidatos para un miembro de:

public class Test 
{ 
    private static void Main() 
    { 
     var ser = new XmlSerializer(typeof (Test)); 
     var obj = new Test {Value = "abc"}; 
     ser.Serialize(Console.Out, obj); 
     obj = new Test { Value = 123 }; 
     ser.Serialize(Console.Out, obj); 
     obj = new Test { Value = 456.7F }; 
     ser.Serialize(Console.Out, obj); 
    } 

    [XmlElement("a", Type = typeof(int))] 
    [XmlElement("b", Type = typeof(string))] 
    [XmlElement("c", Type = typeof(float))] 
    public object Value { get; set; } 
} 

las partes importantes de la salida (ignorando toda la xmlns/<?xml> etc) son:

<Test> 
    <b>abc</b> 
</Test> 

<Test> 
    <a>123</a> 
</Test> 

<Test> 
    <c>456.7</c> 
</Test> 
+0

¡Funciona a la perfección! – user919426

+0

Esto solo funciona si usa diferentes nombres o espacios de nombres para los elementos (a, b, c en este ejemplo). En mi caso, necesitaba que el nombre del elemento siempre fuera el mismo, así que publiqué una respuesta que pareció funcionar para mí. – Evan

0

lo hice implementando la interfaz IXmlSerializable, escribir el tipo de objeto como un atributo del elemento.

public void ReadXml(XmlReader reader) 
    { 
    reader.MoveToContent(); 

    Boolean isEmptyElement = reader.IsEmptyElement; 
    reader.ReadStartElement(); 
    if (!isEmptyElement) 
    { 

     // ...here comes all other properties deserialization 

     object tag; 
     if (ReadXmlObjectProperty(reader, "Tag", out tag)) 
     { 
      Tag = tag; 
     } 
     reader.ReadEndElement(); 
    } 
    } 

    public void WriteXml(XmlWriter writer) 
    { 

    // ...here comes all other properties serialization 

    WriteXmlObjectProperty(writer, "Tag", Tag); 
    } 

    public static bool ReadXmlObjectProperty(XmlReader reader, 
              string name, 
              out object value) 
    { 
    value = null; 

    // Moves to the element 
    while (!reader.IsStartElement(name)) 
    { 
     return false; 
    } 
    // Get the serialized type 
    string typeName = reader.GetAttribute("Type"); 

    Boolean isEmptyElement = reader.IsEmptyElement; 
    reader.ReadStartElement(); 
    if (!isEmptyElement) 
    { 
     Type type = Type.GetType(typeName); 

     if (type != null) 
     { 
      // Deserialize it 
      XmlSerializer serializer = new XmlSerializer(type); 
      value = serializer.Deserialize(reader); 
     } 
     else 
     { 
      // Type not found within this namespace: get the raw string! 
      string xmlTypeName = typeName.Substring(typeName.LastIndexOf('.')+1); 
      value = reader.ReadElementString(xmlTypeName); 
     } 
     reader.ReadEndElement(); 
    } 

    return true; 
    } 
    public static void WriteXmlObjectProperty(XmlWriter writer, 
              string name, 
              object value) 
    { 
    if (value != null) 
    { 
     Type valueType = value.GetType(); 
     writer.WriteStartElement(name); 
     writer.WriteAttributeString("Type", valueType.FullName); 
     writer.WriteRaw(ToXmlString(value, valueType)); 
     writer.WriteFullEndElement(); 
    } 
    } 

    public static string ToXmlString(object item, Type type) 
    { 
    XmlWriterSettings settings = new XmlWriterSettings(); 
    settings.Encoding = Encoding.ASCII; 
    settings.Indent = true; 
    settings.OmitXmlDeclaration = true; 
    settings.NamespaceHandling = NamespaceHandling.OmitDuplicates; 

    using(StringWriter textWriter = new StringWriter()) 
    using(XmlWriter xmlWriter = XmlWriter.Create(textWriter, settings)) 
    { 
     XmlSerializer serializer = new XmlSerializer(type); 
     serializer.Serialize(xmlWriter, item, new XmlSerializerNamespaces(new[] { XmlQualifiedName.Empty })); 
     return textWriter.ToString(); 
    } 
    } 

Nota: en el código no uso espacio de nombres ni codificación ASCII, estas son opciones no obligatorias.

HTH, Cabbi

0

También puede utilizar [XmlInclude(typeof(YourType))] en la claseque contiene la propiedad del objeto. Así, en el caso de la OP, que se vería así

[XmlInclude(typeof(PossibleClassOne))] 
[XmlInclude(typeof(PossibleClassTwo))] 
public class MyClass 
{ 
    public object Tag { get; set; } 
} 

De esta manera, usted puede mantener su nombre del elemento <Tag> en todos los casos

Cuestiones relacionadas