序列化包含多种不明类型的集合
序列化包含多种不明类型的集合
导言:
你是否曾经想过序列化构造对象,它里面有一个集合,这个集合包含接口或者抽象类?你是否不知道所有的你要序列化的类型?好吧,如果这样,那么我可以帮助你。
使用代码:
让我们开始.首先,你将要将采用下面的帮助类,将它粘贴到你的项目之中。它将帮助你只用一行代码就能序列化一个对象为XML。
![](https://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif)
/// <summary>
/// 一个用来序列化的通用类
/// </summary>
public class GenericSerializer
{
/// <summary>
/// Serializes the given object.
/// </summary>
/// <typeparam name="T">The type of the object to be serialized.</typeparam>
/// <param name="obj">The object to be serialized.</param>
/// <returns>String representation of the serialized object.</returns>
public static string Serialize<T>(T obj)
{
XmlSerializer xs = null;
StringWriter sw = null;
try
{
xs = new XmlSerializer(typeof(T));
sw = new StringWriter();
xs.Serialize(sw, obj);
sw.Flush();
return sw.ToString();
}
catch (Exception ex)
{
throw ex;
}
finally
{
if (sw != null)
{
sw.Close();
sw.Dispose();
}
}
}
public static string Serialize<T>(T obj, Type[] extraTypes)
{
XmlSerializer xs = null;
StringWriter sw = null;
try
{
xs = new XmlSerializer(typeof(T), extraTypes);
sw = new StringWriter();
xs.Serialize(sw, obj);
sw.Flush();
return sw.ToString();
}
catch (Exception ex)
{
throw ex;
}
finally
{
if (sw != null)
{
sw.Close();
sw.Dispose();
}
}
}
/// <summary>
/// Serializes the given object.
/// </summary>
/// <typeparam name="T">The type of the object to be serialized.</typeparam>
/// <param name="obj">The object to be serialized.</param>
/// <param name="writer">The writer to be used for output in the serialization.</param>
public static void Serialize<T>(T obj, XmlWriter writer)
{
XmlSerializer xs = new XmlSerializer(typeof(T));
xs.Serialize(writer, obj);
}
/// <summary>
/// Serializes the given object.
/// </summary>
/// <typeparam name="T">The type of the object to be serialized.</typeparam>
/// <param name="obj">The object to be serialized.</param>
/// <param name="writer">The writer to be used for output in the serialization.</param>
/// <param name="extraTypes"><c>Type</c> array
/// of additional object types to serialize.</param>
public static void Serialize<T>(T obj, XmlWriter writer, Type[] extraTypes)
{
XmlSerializer xs = new XmlSerializer(typeof(T), extraTypes);
xs.Serialize(writer, obj);
}
/// <summary>
/// Deserializes the given object.
/// </summary>
/// <typeparam name="T">The type of the object to be deserialized.</typeparam>
/// <param name="reader">The reader used to retrieve the serialized object.</param>
/// <returns>The deserialized object of type T.</returns>
public static T Deserialize<T>(XmlReader reader)
{
XmlSerializer xs = new XmlSerializer(typeof(T));
return (T)xs.Deserialize(reader);
}
/// <summary>
/// Deserializes the given object.
/// </summary>
/// <typeparam name="T">The type of the object to be deserialized.</typeparam>
/// <param name="reader">The reader used to retrieve the serialized object.</param>
/// <param name="extraTypes"><c>Type</c> array
/// of additional object types to deserialize.</param>
/// <returns>The deserialized object of type T.</returns>
public static T Deserialize<T>(XmlReader reader, Type[] extraTypes)
{
XmlSerializer xs = new XmlSerializer(typeof(T), extraTypes);
return (T)xs.Deserialize(reader);
}
/// <summary>
/// Deserializes the given object.
/// </summary>
/// <typeparam name="T">The type of the object to be deserialized.</typeparam>
/// <param name="XML">The XML file containing the serialized object.</param>
/// <returns>The deserialized object of type T.</returns>
public static T Deserialize<T>(string XML)
{
if (XML == null || XML == string.Empty)
return default(T);
XmlSerializer xs = null;
StringReader sr = null;
try
{
xs = new XmlSerializer(typeof(T));
sr = new StringReader(XML);
return (T)xs.Deserialize(sr);
}
catch (Exception ex)
{
throw ex;
}
finally
{
if (sr != null)
{
sr.Close();
sr.Dispose();
}
}
}
public static T Deserialize<T>(string XML, Type[] extraTypes)
{
if (XML == null || XML == string.Empty)
return default(T);
XmlSerializer xs = null;
StringReader sr = null;
try
{
xs = new XmlSerializer(typeof(T), extraTypes);
sr = new StringReader(XML);
return (T)xs.Deserialize(sr);
}
catch (Exception ex)
{
throw ex;
}
finally
{
if (sr != null)
{
sr.Close();
sr.Dispose();
}
}
}
public static void SaveAs<T>(T Obj, string FileName,
Encoding encoding, Type[] extraTypes)
{
if (File.Exists(FileName))
File.Delete(FileName);
DirectoryInfo di = new DirectoryInfo(Path.GetDirectoryName(FileName));
if (!di.Exists)
di.Create();
XmlDocument document = new XmlDocument();
XmlWriterSettings wSettings = new XmlWriterSettings();
wSettings.Indent = true;
wSettings.Encoding = encoding;
wSettings.CloseOutput = true;
wSettings.CheckCharacters = false;
using (XmlWriter writer = XmlWriter.Create(FileName, wSettings))
{
if (extraTypes != null)
Serialize<T>(Obj, writer, extraTypes);
else
Serialize<T>(Obj, writer);
writer.Flush();
document.Save(writer);
}
}
public static void SaveAs<T>(T Obj, string FileName, Type[] extraTypes)
{
SaveAs<T>(Obj, FileName, Encoding.UTF8, extraTypes);
}
public static void SaveAs<T>(T Obj, string FileName, Encoding encoding)
{
SaveAs<T>(Obj, FileName, encoding, null);
}
public static void SaveAs<T>(T Obj, string FileName)
{
SaveAs<T>(Obj, FileName, Encoding.UTF8);
}
public static T Open<T>(string FileName, Type[] extraTypes)
{
T obj = default(T);
if (File.Exists(FileName))
{
XmlReaderSettings rSettings = new XmlReaderSettings();
rSettings.CloseInput = true;
rSettings.CheckCharacters = false;
using (XmlReader reader = XmlReader.Create(FileName, rSettings))
{
reader.ReadOuterXml();
if (extraTypes != null)
obj = Deserialize<T>(reader, extraTypes);
else
obj = Deserialize<T>(reader);
}
}
return obj;
}
public static T Open<T>(string FileName)
{
return Open<T>(FileName, null);
}
}
/// 一个用来序列化的通用类
/// </summary>
public class GenericSerializer
{
/// <summary>
/// Serializes the given object.
/// </summary>
/// <typeparam name="T">The type of the object to be serialized.</typeparam>
/// <param name="obj">The object to be serialized.</param>
/// <returns>String representation of the serialized object.</returns>
public static string Serialize<T>(T obj)
{
XmlSerializer xs = null;
StringWriter sw = null;
try
{
xs = new XmlSerializer(typeof(T));
sw = new StringWriter();
xs.Serialize(sw, obj);
sw.Flush();
return sw.ToString();
}
catch (Exception ex)
{
throw ex;
}
finally
{
if (sw != null)
{
sw.Close();
sw.Dispose();
}
}
}
public static string Serialize<T>(T obj, Type[] extraTypes)
{
XmlSerializer xs = null;
StringWriter sw = null;
try
{
xs = new XmlSerializer(typeof(T), extraTypes);
sw = new StringWriter();
xs.Serialize(sw, obj);
sw.Flush();
return sw.ToString();
}
catch (Exception ex)
{
throw ex;
}
finally
{
if (sw != null)
{
sw.Close();
sw.Dispose();
}
}
}
/// <summary>
/// Serializes the given object.
/// </summary>
/// <typeparam name="T">The type of the object to be serialized.</typeparam>
/// <param name="obj">The object to be serialized.</param>
/// <param name="writer">The writer to be used for output in the serialization.</param>
public static void Serialize<T>(T obj, XmlWriter writer)
{
XmlSerializer xs = new XmlSerializer(typeof(T));
xs.Serialize(writer, obj);
}
/// <summary>
/// Serializes the given object.
/// </summary>
/// <typeparam name="T">The type of the object to be serialized.</typeparam>
/// <param name="obj">The object to be serialized.</param>
/// <param name="writer">The writer to be used for output in the serialization.</param>
/// <param name="extraTypes"><c>Type</c> array
/// of additional object types to serialize.</param>
public static void Serialize<T>(T obj, XmlWriter writer, Type[] extraTypes)
{
XmlSerializer xs = new XmlSerializer(typeof(T), extraTypes);
xs.Serialize(writer, obj);
}
/// <summary>
/// Deserializes the given object.
/// </summary>
/// <typeparam name="T">The type of the object to be deserialized.</typeparam>
/// <param name="reader">The reader used to retrieve the serialized object.</param>
/// <returns>The deserialized object of type T.</returns>
public static T Deserialize<T>(XmlReader reader)
{
XmlSerializer xs = new XmlSerializer(typeof(T));
return (T)xs.Deserialize(reader);
}
/// <summary>
/// Deserializes the given object.
/// </summary>
/// <typeparam name="T">The type of the object to be deserialized.</typeparam>
/// <param name="reader">The reader used to retrieve the serialized object.</param>
/// <param name="extraTypes"><c>Type</c> array
/// of additional object types to deserialize.</param>
/// <returns>The deserialized object of type T.</returns>
public static T Deserialize<T>(XmlReader reader, Type[] extraTypes)
{
XmlSerializer xs = new XmlSerializer(typeof(T), extraTypes);
return (T)xs.Deserialize(reader);
}
/// <summary>
/// Deserializes the given object.
/// </summary>
/// <typeparam name="T">The type of the object to be deserialized.</typeparam>
/// <param name="XML">The XML file containing the serialized object.</param>
/// <returns>The deserialized object of type T.</returns>
public static T Deserialize<T>(string XML)
{
if (XML == null || XML == string.Empty)
return default(T);
XmlSerializer xs = null;
StringReader sr = null;
try
{
xs = new XmlSerializer(typeof(T));
sr = new StringReader(XML);
return (T)xs.Deserialize(sr);
}
catch (Exception ex)
{
throw ex;
}
finally
{
if (sr != null)
{
sr.Close();
sr.Dispose();
}
}
}
public static T Deserialize<T>(string XML, Type[] extraTypes)
{
if (XML == null || XML == string.Empty)
return default(T);
XmlSerializer xs = null;
StringReader sr = null;
try
{
xs = new XmlSerializer(typeof(T), extraTypes);
sr = new StringReader(XML);
return (T)xs.Deserialize(sr);
}
catch (Exception ex)
{
throw ex;
}
finally
{
if (sr != null)
{
sr.Close();
sr.Dispose();
}
}
}
public static void SaveAs<T>(T Obj, string FileName,
Encoding encoding, Type[] extraTypes)
{
if (File.Exists(FileName))
File.Delete(FileName);
DirectoryInfo di = new DirectoryInfo(Path.GetDirectoryName(FileName));
if (!di.Exists)
di.Create();
XmlDocument document = new XmlDocument();
XmlWriterSettings wSettings = new XmlWriterSettings();
wSettings.Indent = true;
wSettings.Encoding = encoding;
wSettings.CloseOutput = true;
wSettings.CheckCharacters = false;
using (XmlWriter writer = XmlWriter.Create(FileName, wSettings))
{
if (extraTypes != null)
Serialize<T>(Obj, writer, extraTypes);
else
Serialize<T>(Obj, writer);
writer.Flush();
document.Save(writer);
}
}
public static void SaveAs<T>(T Obj, string FileName, Type[] extraTypes)
{
SaveAs<T>(Obj, FileName, Encoding.UTF8, extraTypes);
}
public static void SaveAs<T>(T Obj, string FileName, Encoding encoding)
{
SaveAs<T>(Obj, FileName, encoding, null);
}
public static void SaveAs<T>(T Obj, string FileName)
{
SaveAs<T>(Obj, FileName, Encoding.UTF8);
}
public static T Open<T>(string FileName, Type[] extraTypes)
{
T obj = default(T);
if (File.Exists(FileName))
{
XmlReaderSettings rSettings = new XmlReaderSettings();
rSettings.CloseInput = true;
rSettings.CheckCharacters = false;
using (XmlReader reader = XmlReader.Create(FileName, rSettings))
{
reader.ReadOuterXml();
if (extraTypes != null)
obj = Deserialize<T>(reader, extraTypes);
else
obj = Deserialize<T>(reader);
}
}
return obj;
}
public static T Open<T>(string FileName)
{
return Open<T>(FileName, null);
}
}
现在我们已经了解这个方法了,让我们看看我们的问题所在。举一个例子,我们有一个抽象类,Animal。然后有两个继承Animal的类:Human和Dog。
![](https://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif)
public abstract class Animal
{
public abstract int Legs
{
get;
set;
}
public abstract bool HasTail
{
get;
set;
}
}
public class Human : Animal
{
public Human()
{
this.Legs = 2;
this.HasTail = false;
}
public override int Legs
{
get;
set;
}
public override bool HasTail
{
get;
set;
}
}
public class Dog : Animal
{
public Dog()
{
this.Legs = 4;
this.HasTail = true;
this.Breed = "Black Lab";
}
public override int Legs
{
get;
set;
}
public override bool HasTail
{
get;
set;
}
public string Breed
{
get;
set;
}
}
{
public abstract int Legs
{
get;
set;
}
public abstract bool HasTail
{
get;
set;
}
}
public class Human : Animal
{
public Human()
{
this.Legs = 2;
this.HasTail = false;
}
public override int Legs
{
get;
set;
}
public override bool HasTail
{
get;
set;
}
}
public class Dog : Animal
{
public Dog()
{
this.Legs = 4;
this.HasTail = true;
this.Breed = "Black Lab";
}
public override int Legs
{
get;
set;
}
public override bool HasTail
{
get;
set;
}
public string Breed
{
get;
set;
}
}
现在我们有一个想要序列化的对象。它是HouseHold,它的有一个Animal集合属性:
public class HouseHold
{
public HouseHold()
{
this.Residents = new List<Animal>();
}
public List<Animal> Residents
{
get;
set;
}
}
{
public HouseHold()
{
this.Residents = new List<Animal>();
}
public List<Animal> Residents
{
get;
set;
}
}
现在,我们序列化这个对象。
![](https://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif)
try
{
HouseHold hh = new HouseHold();
hh.Residents.Add(new Human());
hh.Residents.Add(new Human());
hh.Residents.Add(new Dog());
string xml = GenericSerializer.Serialize<HouseHold>(hh);
Console.WriteLine(xml);
}
catch (Exception ex)
{
Console.WriteLine(ex.ToString());
}
Console.Read();
{
HouseHold hh = new HouseHold();
hh.Residents.Add(new Human());
hh.Residents.Add(new Human());
hh.Residents.Add(new Dog());
string xml = GenericSerializer.Serialize<HouseHold>(hh);
Console.WriteLine(xml);
}
catch (Exception ex)
{
Console.WriteLine(ex.ToString());
}
Console.Read();
我们将会得到下面的Exception的Message:
"生成 XML 文档时出错。
下面是 InnerException的Message :
"不应是类型 Kirin.Human。使用 XmlInclude 或 SoapInclude 属性静态指定非已知的类型。"
这里有个关键字“静态”, 我们可以在每个类的顶部放置XmlInclude属性解决这个问题,如:
![](https://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif)
[XmlInclude(typeof(Animal)), XmlInclude(typeof(Human))]
public class HouseHold
{
public HouseHold()
{
this.Residents = new List<Animal>();
}
public List<Animal> Residents
{
get;
set;
}
}
public class HouseHold
{
public HouseHold()
{
this.Residents = new List<Animal>();
}
public List<Animal> Residents
{
get;
set;
}
}
但如果我们不知道的继承Animal的所有类,如果我们的应用是可延展的,需要能够使用由用户自定义类型,上面的代码是解决不了问题的。让我们向下看,我会告诉你如何可以做到的。
首先,我们要做的是使HouseHold类实现IXmlSerializable接口,这样我们就可以拦截这个对象的序列化,我们自己去序列它。
接着,我们构建一个方法,能检查所有的类型,在这个例子中,我们使用反射去查询的所有继承了Animal类的程序集。
![](https://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif)
public class HouseHold : IXmlSerializable
{
private static Type[] _animalTypes;
static HouseHold()
{
_animalTypes = GetAnimalTypes().ToArray();
}
public HouseHold()
{
this.Residents = new List<Animal>();
}
public List<Animal> Residents
{
get;
set;
}
#region IXmlSerializable Members
public System.Xml.Schema.XmlSchema GetSchema()
{
return null;
}
public void ReadXml(System.Xml.XmlReader reader)
{
bool wasEmpty = reader.IsEmptyElement;
reader.Read();
if (wasEmpty)
return;
reader.MoveToContent();
reader.ReadStartElement("Residents");
this.Residents = GenericSerializer.Deserialize<List<Animal>>(reader, _animalTypes);
reader.ReadEndElement();
//Read Closing Element
reader.ReadEndElement();
}
public void WriteXml(System.Xml.XmlWriter writer)
{
writer.WriteStartElement("Residents");
GenericSerializer.Serialize<List<Animal>>(this.Residents, writer, _animalTypes);
writer.WriteEndElement();
}
#endregion
public static List<Type> GetAnimalTypes()
{
List<Type> types = new List<Type>();
Assembly asm = typeof(HouseHold).Assembly;
Type tAnimal = typeof(Animal);
//Query our types. We could also load any other assemblies and
//query them for any types that inherit from Animal
foreach (Type currType in asm.GetTypes())
{
if (!currType.IsAbstract
&& !currType.IsInterface
&& tAnimal.IsAssignableFrom(currType))
types.Add(currType);
}
return types;
}
}
{
private static Type[] _animalTypes;
static HouseHold()
{
_animalTypes = GetAnimalTypes().ToArray();
}
public HouseHold()
{
this.Residents = new List<Animal>();
}
public List<Animal> Residents
{
get;
set;
}
#region IXmlSerializable Members
public System.Xml.Schema.XmlSchema GetSchema()
{
return null;
}
public void ReadXml(System.Xml.XmlReader reader)
{
bool wasEmpty = reader.IsEmptyElement;
reader.Read();
if (wasEmpty)
return;
reader.MoveToContent();
reader.ReadStartElement("Residents");
this.Residents = GenericSerializer.Deserialize<List<Animal>>(reader, _animalTypes);
reader.ReadEndElement();
//Read Closing Element
reader.ReadEndElement();
}
public void WriteXml(System.Xml.XmlWriter writer)
{
writer.WriteStartElement("Residents");
GenericSerializer.Serialize<List<Animal>>(this.Residents, writer, _animalTypes);
writer.WriteEndElement();
}
#endregion
public static List<Type> GetAnimalTypes()
{
List<Type> types = new List<Type>();
Assembly asm = typeof(HouseHold).Assembly;
Type tAnimal = typeof(Animal);
//Query our types. We could also load any other assemblies and
//query them for any types that inherit from Animal
foreach (Type currType in asm.GetTypes())
{
if (!currType.IsAbstract
&& !currType.IsInterface
&& tAnimal.IsAssignableFrom(currType))
types.Add(currType);
}
return types;
}
}
现在,我们运行我们的测试代码,能顺利通过。希望对你有所帮助,谢谢!