• 模仿.NET的序列化机制


    大家都使用过.NET的序列化机制。只要给类型标上   [Serializable] 特性,即可将任何数据对象转化为二进制数据形式,以方便保存或传输。并且使用起来非常方便。下面是一个最简单的例子:

    //写数据
    using ( FileStream writer =new FileStream( fileName, FileMode.Create ) )
    {
    IFormatter formatter
    =new BinaryFormatter();
    formatter.Serialize( writer, data );
    }
    //读数据
    using ( FileStream reader =new FileStream( fileName, FileMode.Open ) )
    {
    IFormatter formatter
    =new BinaryFormatter();
    return formatter.Deserialize( reader ) as T;
    }

    用序列化机制,就不需要用户去考虑特定的数据结构,提供了一般化的数据保存方式,此功能非常强大。由于自己不满足仅仅会使用,于是就想能否自己也实现个类似的功能呢?

    ——于是就有了下面的XmlSave类,这个类将用户自定义的类型以XML格式保存到文件中并可以从文件中恢复对象。

    先看其使用方法(下面的Student类型是自定义类型的代表,其属性不具有实际意义):

    class Student
    {
    [Save(
    "" )]
    publicstring Name { get; set; }
    [Save(
    "" )]
    public List<int> Number { get; set; }
    }
    ……
    //写入
    XmlSave x =new XmlSave( "d:\\a.txt" );
    Student s
    =new Student();
    s.Name
    ="xbc";
    s.Number
    =new List<int>();
    s.Number.Add(
    0 );
    s.Number.Add(
    1 );
    x.Wirte(s);
    //读取
    XmlSave x =new XmlSave( "d:\\a.txt" );
    Student t
    = x.Read() as Student;

    XmlSave类使用.NET的反射机制,自动解析出类型的结构,并保存到XML文件中。

    上面这个例子生成的XML文件如下:Student类型中的属性有个Save()特性,这是个自定义的特性,只所以需要这个类型是因为可以让用户决定该保存类型的哪些属性,只有加上这个标志的属性才会被保存到文件中。

    <?xml version="1.0" standalone="yes"?>
    <root Type="XBC.Student" Name="xbc">
    <Number IsList="True">
    <Item>0</Item>
    <Item>1</Item>
    </Number>
    </root>

    现阶段,XmlSave可以保存的类型可分为四种:

    一、"int",即.NET内置的简单类型,如:System.Boolean,Byte,SByte,Char,Decimal,Double,Int32,String,Enum,Guid……

    int
    <?xml version="1.0" standalone="yes"?><root Type="System.Int32"><Item>4</Item></root>

    二、"List<int>"简单类型的List<T>泛型集合

    List
    <?xml version="1.0" standalone="yes"?>
    <root IsList="True" Type="System.Collections.Generic.List`1[[System.Int32, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]]">
    <Item>1</Item>
    <Item>3</Item>
    <Item>5</Item>
    </root>

    三、"Student"自定义类型

    Student
    <?xml version="1.0" standalone="yes"?>
    <root Type="XBC.Student" Name="xbc" Age="23"/>

    四、"List<Student>"自定义类型的泛型List<T>类型

    List
    <?xml version="1.0" standalone="yes"?>
    <root IsList="True" Type="System.Collections.Generic.List`1[[XBC.Student, XmlSave, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null]]">
    <Index Name="xbc" Age="23"/>
    <Index Name="fvju" Age="20"/>
    </root>

    并且在自定义类型时上面这四种情况可以嵌套使用。

    例如下面的Student类型:

    复杂的Student类型
    class Student
    {
    [Save(
    "" )]
    publicstring Name { get; set; }
    List<StudentTime> _times =new List<StudentTime>();
    [Save( "" )]
      public List<StudentTime> Times
    {
    get { return _times; }
    set { _times = value; }
    }
    }
    class StudentTime
    {
    [Save(
    "" )]
    publicint Year { get; set; }
    [Save(
    "" )]
    publicint Month { get; set; }
    }

    然后再定义List<Student> ss对象,生成的XML文件格式为:

    List
    <?xml version="1.0" standalone="yes"?>
    <root IsList="True" Type="System.Collections.Generic.List`1[[XBC.Student, XmlSave, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null]]">
    <Index Name="xbc">
    <Times IsList="True" Type="XBC.StudentTime">
    <Index Year="2007" Month="9"/>
    <Index Year="2011" Month="3"/>
    </Times>
    </Index>
    <Index Name="fvju">
    <Times IsList="True" Type="XBC.StudentTime">
    <Index Year="2009" Month="4"/>
    </Times>
    </Index>
    </root>

    现在已介绍了XmlSave的使用方法,下面将介绍其实现。由于实现起来比较复杂,所以本人选取几个比较重要的方面讲解,详细请见源代码

    一、保存数据

      1.先判断这个数据是否为List<T>,用函数IsList判断,这个函数内部考查Type对象是否实现IList接口。

        若为List<T>类型,再判断T是简单类型(int)还是复杂类型(Student),简单List<T>用WriteBaseList写入文件,复杂List<T>用函数WriteComplexList写入文件。

        若为单个数据类型,则需要判断这个数据类型是自定义的复合类型(Student),还是.NET的内置简单类型,并采用不同的方式处理。

        在写入功能中最重要的一个函数为:

    WirteOne
    privatevoid WriteOne( XmlDocument document, XmlElement note, object item )
    {
    PropertyInfo[] ps
    = item.GetType().GetProperties();

    //遍历这个类的所有属性
    foreach ( PropertyInfo p in ps )
    {
    object value = p.GetValue( item, null );
    //查找这个对象下面被标志为SaveAttribute的特性
    if ( IsSave( p ) && value !=null )
    {
    if ( IsList( p.PropertyType ) )
    {
    //List<Notify>
    if ( IsHaveChild( p.PropertyType.GetGenericArguments()[0] ) )
    // if ( IsHaveChild( p.PropertyType ) )
    {
    var a
    = value as IList;
    if ( a.Count !=0 )
    {
    XmlElement e
    = document.CreateElement( p.Name );
    e.SetAttribute(
    "IsList", "True" );
    e.SetAttribute(
    "Type", p.PropertyType.GetGenericArguments()[0].FullName );
    // e.SetAttribute( "Type", p.PropertyType.FullName );
    WriteComplexList( document, e, value );
    note.AppendChild( e );
    }
    }
    //List<int>
    else
    {
    var s
    = value as IList;
    if ( s.Count !=0 )
    {
    XmlElement list
    = document.CreateElement( p.Name );
    list.SetAttribute(
    "IsList", "True" );
    WriteBaseList( document, list, value );
    note.AppendChild( list );
    }
    }

    }
    //单个
    else
    {
    //复杂类型 Notify
    if ( IsHaveChild( p.PropertyType ) )
    {
    XmlElement e
    = document.CreateElement( p.Name );
    e.SetAttribute(
    "Type", p.PropertyType.FullName );
    WriteOne( document, e, value );
    note.AppendChild( e );
    }
    //简单类型 int
    else
    {
    XmlAttribute attr
    = document.CreateAttribute( p.Name );
    attr.Value
    = p.GetValue( item, null ).ToString();
    note.SetAttributeNode( attr );
    }
    }

    }
    }
    }

    这个函数的document参数即为XML文档,item为一个自定义的类型对象(如s:Student),note为XML文档中一个元素结点,即item数据所在XML文档的位置。

    此函数扫描类型的所有属性,先考查属性是否实现了SaveAttribute特性,再分别讨论属性的类型是上面所列四种类型中的哪一种,分别进行不同的处理。

    二、读取数据

    与保存数据类似,也必须分四种情况对数据进行讨论。与保存数据不同的是,在将值写入内存对象有点区别,因为写入XML文件,可直接使用.NET提供的XML处理函数来实现,而读取时就需要用.NET的反射机制来给对象赋值。

    SetProperty函数实现的功能为:给对象o的name属性,赋值为value。

    SetProperty
    void SetProperty( object o, object value, string name )
    {
    Type type
    = o.GetType();
    PropertyInfo p
    = type.GetProperty( name );
    if ( p !=null )
    {
    p.SetValue( o, value,
    null );
    }
    }

    下面的ReadBaseList函数实现的功能为:从XML的一系列并列节点中,创建List<T>类型的对象,其中type参数为List<T>类型的字符串表示。

    ReadBaseList
    object ReadBaseList( XmlNodeList nodes, string type )
    {
    object result = CreateObject( type );
    MethodInfo m
    = result.GetType().GetMethod( "Add" );
    //ArrayList result = new ArrayList();
    foreach ( XmlNode i in nodes )
    {
    m.Invoke( result,
    newobject[] { ConvertType( i.InnerText, GetListPropertyType( result.GetType() ) ) } );
    }
    return result;
    }

    此函数的关键是于获得List<T>类型的Add方法,将其存储在MethInfo对象中,用此对象的Invoke方法向List<T>对象中添加元素。

    现在XmlSave还不完善,比如不支持数组、不支持Stack<T>等类型的数据。并且由于使用了大量的反射,使得这个类在使用时非常耗时。现在的思路是想将反射的运行转移到设计阶段,即设计一个自动代码生成器,使之可以根据要保存的数据类型生成针对专门数据类型的XML读写代码(这个过程非常有挑战,暂时还没有去思考如何实现)。

    到此简略介绍了XmlSave的实现,详细请见源代码

    如果需要在其他地方使用本程序中的代码,请保留原作者信息。

  • 相关阅读:
    Study Plan The Twelfth Day
    Study Plan The Fifteenth Day
    Study Plan The Seventeenth Day
    Study Plan The Tenth Day
    Study Plan The Eighth Day
    Study Plan The Eleventh Day
    Study Plan The Sixteenth Day
    Study Plan The Thirteenth Day
    Study Plan The Fourteenth Day
    Study Plan The Ninth Day
  • 原文地址:https://www.cnblogs.com/xiangism/p/1992245.html
Copyright © 2020-2023  润新知