zoukankan      html  css  js  c++  java
  • 模仿.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的实现,详细请见源代码

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

  • 相关阅读:
    jquery on()动态绑定元素的的点击事件无反应的问题记录
    【分享】开源富文本编辑器之间的较量
    【分享】JS如何为复制的Web文本添加其他信息
    HTML table导出到Excel中的解决办法
    Vue跨门槛系列之实例的阐述
    Vue.js 基本功能了解一下~
    JS数据结构的栈和队列操作
    CSS宽度高度的百分比取值基于谁
    CSS个人笔记
    使用word设置标题级别, 自动生成和大纲对应的多级列表, 自动生成索引目录
  • 原文地址:https://www.cnblogs.com/xiangism/p/1992245.html
Copyright © 2011-2022 走看看