序列化包含多种不明类型的集合

序列化包含多种不明类型的集合

 代码:/Files/zhuqil/Kirin.rar

导言:

   你是否曾经想过序列化构造对象,它里面有一个集合,这个集合包含接口或者抽象类?你是否不知道所有的你要序列化的类型?好吧,如果这样,那么我可以帮助你。

使用代码:

   让我们开始.首先,你将要将采用下面的帮助类,将它粘贴到你的项目之中。它将帮助你只用一行代码就能序列化一个对象为XML。

 

代码
/// <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);
    }
}

 

    现在我们已经了解这个方法了,让我们看看我们的问题所在。举一个例子,我们有一个抽象类,Animal。然后有两个继承Animal的类:Human和Dog。

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

 

    现在我们有一个想要序列化的对象。它是HouseHold,它的有一个Animal集合属性:

public class HouseHold
{
    
public HouseHold()
    {
        
this.Residents = new List<Animal>();
    }

    
public List<Animal> Residents
    {
        
get;
        
set;
    }
}

 

   现在,我们序列化这个对象。

代码
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();

 

我们将会得到下面的Exception的Message:

"生成 XML 文档时出错。

 

下面是 InnerException的Message :

"不应是类型 Kirin.Human。使用 XmlInclude 或 SoapInclude 属性静态指定非已知的类型。"

 

这里有个关键字“静态”, 我们可以在每个类的顶部放置XmlInclude属性解决这个问题,如:

代码
   [XmlInclude(typeof(Animal)), XmlInclude(typeof(Human))]
    
public class HouseHold
    {
        
public HouseHold()
        {
            
this.Residents = new List<Animal>();
        }
     
        
public List<Animal> Residents
        {
            
get;
            
set;
        }
    }

 

  但如果我们不知道的继承Animal的所有类,如果我们的应用是可延展的,需要能够使用由用户自定义类型,上面的代码是解决不了问题的。让我们向下看,我会告诉你如何可以做到的。

   首先,我们要做的是使HouseHold类实现IXmlSerializable接口,这样我们就可以拦截这个对象的序列化,我们自己去序列它。

  接着,我们构建一个方法,能检查所有的类型,在这个例子中,我们使用反射去查询的所有继承了Animal类的程序集。 

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

    现在,我们运行我们的测试代码,能顺利通过。希望对你有所帮助,谢谢!

posted @ 2009-12-16 08:32  tangself  阅读(350)  评论(0编辑  收藏  举报