如何使用XmlSerializer序列化Object类型的属性



我有一个属性:

public object Tag

但是它可以包含有限数量的类型,不幸的是没有基类型(对象类型除外)。但是当我用这个属性序列化对象时,它不会被序列化。有没有一种方法可以用可能的类型指示XmlSerializer?

我不建议这样做,但可以使用[XmlElement]等来告诉它一个成员的多种候选类型:

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

输出的重要位(忽略所有xmlns/<?xml>等)为:

<Test>
  <b>abc</b>
</Test>
<Test>
  <a>123</a>
</Test>
<Test>
  <c>456.7</c>
</Test>

我实现了IXmlSerializable接口,将对象类型写入元素属性。

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

注意:在代码中,我没有使用名称空间和ASCII编码,这些都是非强制性的选择。

HTH,Cabbi

您也可以在包含对象属性的上使用[XmlInclude(typeof(YourType))]。所以在OP的情况下,它看起来像这个

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

这样,在任何情况下都可以保留元素名称<Tag>

最新更新