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

时间:2012-02-29 10:22:13

标签: c# .net .net-4.0 xmlserializer

我有一个属性:

public object Tag

但它可以包含有限数量的类型,遗憾的是没有基类型(对象类型除外)。但是,当我使用此属性序列化对象时,它不会被序列化。有没有办法用可能的类型指示XmlSerializer?

3 个答案:

答案 0 :(得分:10)

我不建议这样做,但是,您可以使用[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>

答案 1 :(得分:1)

我实现了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

答案 2 :(得分:0)

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

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

这样,您可以在所有情况下保留元素名称<Tag>