Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to serialize property of type Object with XmlSerializer

I have a property:

public object Tag

but it can contain finite number of types, unfortunately without base type (except object type). But when I serialize the object with this property, it doesn't get serialized. Is there a way to instruct XmlSerializer with possible types?

like image 286
dragonfly Avatar asked Feb 29 '12 10:02

dragonfly


People also ask

Which class should be used to serialize an object in XML format?

Xml. Serialization namespace) class is used to serialize and deserialize. The class method Serialize is called. Since we have to serialize in a file, we create a " TextWriter ".


2 Answers

I don't recommend this, but yes, you can use [XmlElement] etc to tell it about multiple candidate types for a member:

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

The important bits of the output (ignoring all the xmlns / <?xml> etc) are:

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

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

<Test>
  <c>456.7</c>
</Test>
like image 178
Marc Gravell Avatar answered Nov 07 '22 05:11

Marc Gravell


I did it implementing the IXmlSerializable interface, writing the object type as an element attribute.

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

Note: in the code I use no namespace and ASCII encoding, those are non mandatory choices.

HTH, Cabbi

like image 23
cabbi Avatar answered Nov 07 '22 07:11

cabbi