2012-02-29 12 views
6

Ho un immobile:come serializzare proprietà di tipo Oggetto con XmlSerializer

public object Tag 

ma può contenere numero finito di tipi, purtroppo senza tipo di base (tranne tipo di oggetto). Ma quando serializzo l'oggetto con questa proprietà, non viene serializzato. C'è un modo per istruire XmlSerializer con possibili tipi?

+0

mi aspetto si potrebbe implementare ISerializable e controllare la serializzazione del oggetto a seconda del tipo di tag, trasmettendolo al suo tipo serializzabile. Tuttavia, non so come deserializzare l'oggetto in un secondo momento. Forse non è una buona idea serializzare le cose di tipo oggetto. –

+1

@Adrian 'XmlSerializer' non si preoccupa di' ISerializable'; tuttavia, sono d'accordo sul fatto che questo scenario è meglio evitare semplicemente –

+0

@Marc XmlSerializer non si preoccupa di ISerialiable, tuttavia si preoccupa di IXmlSerializable e chiamerà i metodi di lettura e scrittura su un oggetto che lo implementa. – Fen

risposta

9

Non consiglio questo, ma sì, è possibile utilizzare [XmlElement] ecc per dire di lui circa più tipi di candidati per un membro:

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

I bit importanti della uscita (ignorando tutte le xmlns/<?xml> etc) sono:

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

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

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

Funziona perfettamente! – user919426

+0

Funziona solo se si utilizzano nomi o spazi dei nomi diversi per gli elementi (a, b, c in questo esempio). Nel mio caso, avevo bisogno che il nome dell'elemento fosse sempre lo stesso, quindi ho postato una risposta che sembrava funzionare per me. – Evan

0

ho fatto implementando l'interfaccia IXmlSerializable, scrivendo il tipo di oggetto come attributo 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: nel codice non utilizzo spazio dei nomi e codifica ASCII, quelle sono scelte non obbligatorie.

HTH, Cabbi

0

È inoltre possibile utilizzare [XmlInclude(typeof(YourType))] sul classe che contiene la proprietà dell'oggetto. Quindi, nel caso del PO, sarebbe simile a questa

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

In questo modo, è possibile mantenere il nome dell'elemento <Tag> in tutti i casi

Problemi correlati