XML万能数据库设计

XML万能数据库设计

 

使用unity开发存取本地数据一般用xml,来实现跨平台的数据存取。为什么不用sqlite我就不解释了,谁用谁知道。

好进入正题,如果你了解hibernate,应该知道他是针对model层数据持久化操作的利器。什么意思呢,也就是说任意对象的增删改查它都帮你做了,你需要做的就是配置一下即可。使用时直接调用提供的接口。JavaC#都有这样的利器。

但是hibernate一般用于web应用,需要处理大量的实体类,虽然用unity开发的游戏也需要对一些类进行持久化操作,但是用hibernate还是太专业了,大材小用不说,好像并不怎么适用。配置什么的应该很烦。但是还想一劳永逸,只写一个操作适用所有对象的增删改查怎么办?自己动手丰衣足食。

首先要知道hibernate是怎么实现的类的自动拆装的,反射。OK,接下来就简单了,知道了方案,接下来就是具体的实现了。楼主看了下C#的反射,没想到实在是太好用了!

好,整理一下基本思路,类的名称作为表名,类的成员变量作为列名,成员变量的值作为值,进行存取。等等,xml又不是sql,这表,列,值代表什么意思呢?如果你学过xml的话,应该知道根,元素。(小白先去学基础知识吧),所以我们存取对象就是把这个对象的名称作为根,字段作为子根进行存取。

以上是xml实现基础,如何做到万能,还需要结合反射,通过反射可以获取的信息:类的名称,类的成员变量,值,等等。所以,存取对象的时候,先将这个类进行解析,获取类名,他的成员变量,以及值,存起来,然后插入的时候,以类名为根,成员变量作为元素,值作为元素的值插入。因为xml存的是字符,所以类型在存的时候都转成字符串。取的时候再将字符串转成相应的类型,封装成对象即可。

不知道听不听的懂,代码是最通俗的,好,进入正题。

首先,你要处理的是泛型对象,这样在操作的时候不需要强转。先写个接口声明一下。插入的时候直接传对象,查找的时候也传对象(默认按ID查找,需要在代码中对ID赋值),更新有两种,一是更新指定的列,二是更新对象的所有列。删除直接传对象。

 

using System.Collections.Generic;
using System;
public interface DataBase {

    void Insert<T>(T t);
    T Select<T>(T t);
    List<T> SelectAll<T>(T t);
    void Update<T>(T t,string key);
    void Update<T>(T t);
    void Delete<T>(T t);
    void CreateData(string path);
}

然后,写个静态的类,使用这个万能xml,代码也很简单,如果有疑惑,我稍后解答,例如为什么写成静态的?

 

using System.Collections.Generic;
using System;
using UnityEngine;
public class MyDataBase
{

    private static DataBase database;
    public static string RESDATAPATH = Application.streamingAssetsPath;
    //源数据目录
    private static string DATAPATH = Application.dataPath;
    //数据目录
    static MyDataBase() {

        
#if UNITY_ANDROID
        DATAPATH = Application.persistentDataPath;

#endif
        database = new XmlDataBase(DATAPATH);
    }

    public static void Insert<T>(T t)
    {
        database.Insert(t);
    
    }
    public static T Select<T>(T t)
    {
        return database.Select(t);
    }
    public static List<T> SelectAll<T>(T t)
    {
        return database.SelectAll<T>(t);
    }
    public static void Update<T>(T t, string key)
    {
        database.Update(t, key);
    }
    public static void Update<T>(T t)
    {
        database.Update(t);
    }
    public static void Delete<T>(T t)
    {
        database.Delete(t);
    }
    
}

 

  好,下面就是xml的存取操作了,不怎么难,关键是一些方法的运用。

using UnityEngine;
using System.Xml;
using System.Collections.Generic;
using System.IO;
using System;
public class XmlDataBase : DataBase
{
    private string path = "/DataBase/GameData.xml";
    private  string Myroot = "MyData";
    private  XmlDocument xmlDoc = new XmlDocument();
    public static string ObjectID="ID";
    public static string PATH;
    private void ReadFile(string path) {

        PATH = path + this.path;
        if (!File.Exists(PATH))//如果指定的路径不存在
        {
            if (!File.Exists(MyDataBase.RESDATAPATH + this.path))//如果不存在源文件
            {
                Directory.CreateDirectory(MyDataBase.RESDATAPATH + "/DataBase");

                File.CreateText(PATH);
                CreateData((MyDataBase.RESDATAPATH + this.path));
                Application.Quit();
            }
            else {

                Directory.CreateDirectory(path + "/DataBase");

                File.CreateText(PATH);
                File.Copy(MyDataBase.RESDATAPATH + this.path, PATH);

                File.Delete(MyDataBase.RESDATAPATH + this.path);

            }
        }
        else
        {
            xmlDoc.Load(PATH);
            
        }
    }
    public XmlDataBase()
    {
        //这是一个XML数据库,基于XML的对象的存取,改查操作。
        //注意,数据对象的ID需要以字母开头,且不能有特殊字符。

        //TextAsset textAsset = (TextAsset)Resources.Load(file, typeof(TextAsset));

        ReadFile(path);//读取默认路径
    }
    public XmlDataBase(string path)
    {
        ReadFile(path);//指定读取默认路径
    }
    public void CreateData(string path)
    {
        
        XmlDocument xmlDoc = new XmlDocument();
        XmlDeclaration xmlDeclaration = xmlDoc.CreateXmlDeclaration("1.0", "utf-8", null);
        XmlNode root = xmlDoc.CreateElement(Myroot);
        xmlDoc.AppendChild(xmlDeclaration);
        xmlDoc.AppendChild(root);
        xmlDoc.Save(path);

    }
    public void Insert<T>(T obj)
    {
        //插入一个对象
        //对象有字段,属性值
        //利用数据的特点,即名称不同,优化查询速度,具体的做法为,将属性值作为子根操作。

        ObjectPara<T> OP = new ObjectPara<T>(obj);
        //

        XmlNode root = xmlDoc.SelectSingleNode(Myroot);//查找<game data>

        XmlNode Myroots = root.SelectSingleNode(OP.ObjName);//查询对象表
        if (Myroots == null) {
            Myroots = xmlDoc.CreateElement(OP.ObjName);
            root.AppendChild(Myroots);
        }

        //以对象表为根,创建一个以ID为根的子根
        XmlNode node = Myroots.SelectSingleNode(OP.GetKeyValue(ObjectID).ToString());
        if (node != null) {//如果待插入的对象已经存在,则将此数据删除后,再重新插入
            Delete<T>(obj);
            Insert<T>(obj);
            return;
        }

        XmlElement xe1 = xmlDoc.CreateElement(OP.GetKeyValue(ObjectID).ToString());//创建一个<data>节点

        for (int i = 0; i < OP.cols.Length; i++)
        {
            
            XmlElement xe = xmlDoc.CreateElement(OP.cols[i]);
            xe.InnerText = OP.values[i];
            xe1.AppendChild(xe);

        }

        if (Myroots == null)
        {
            Myroots = xmlDoc.CreateElement(OP.ObjName);
            root.AppendChild(Myroots);
        }
        else
        {
            Myroots.AppendChild(xe1);
        }
        //添加到<bookstore>节点中
        xmlDoc.Save(PATH);
        OP = null;
    }
    public List<T> SelectAll<T>(T obj)
    {

        List<T> list = new List<T>();

        ObjectPara<T> OP = new ObjectPara<T>(obj);
        
        XmlNode root = xmlDoc.SelectSingleNode(Myroot);//查找<game data>

        XmlNode Myroots = root.SelectSingleNode(OP.ObjName);//查询对象表

        XmlNodeList nodes = Myroots.ChildNodes;//获取对象的所有子对象
        foreach (XmlNode node in nodes)//
        {
            XmlNodeList lis = node.ChildNodes;//获取每个对象的字段
            T o = obj;
            int count = lis.Count;
            string[] values = new string[count];
            for (int i = 0; i < lis.Count; i++) {
                values[i] = lis.Item(i).InnerText.Trim();
            }
            o = OP.GetObject(values);
            list.Add(o);
            
        }

        return list;
    }
    public T Select<T>(T obj)
    {
        ObjectPara<T> OP = new ObjectPara<T>(obj);

        XmlNode root = xmlDoc.SelectSingleNode(Myroot);//查找<game data>

        XmlNode Myroots = root.SelectSingleNode(OP.ObjName);//查询对象表

        XmlNode node = Myroots.SelectSingleNode(OP.GetKeyValue(ObjectID).ToString());
        //获取指定列为根的根元素
        if (node != null)
        {
            XmlNodeList list = node.ChildNodes;//获取根的所有字段
            int count = list.Count;
            string[] values = new string[count];
            for (int i = 0; i < list.Count; i++)
            {//成员变量
                //将遍历出的值赋予数组


                values[i] = list.Item(i).InnerText.Trim();

            }
            obj = OP.GetObject(values);
        }
        else {
            obj = default(T);
            MyTool.P(213);
        } 
        return obj;
    }
    public void Update<T>(T obj)
    {
        ObjectPara<T> OP = new ObjectPara<T>(obj);
        //

        XmlNode root = xmlDoc.SelectSingleNode(Myroot);//查找<game data>

        XmlNode Myroots = root.SelectSingleNode(OP.ObjName);//查询对象表

        XmlNode node = Myroots.SelectSingleNode(OP.GetKeyValue(ObjectID).ToString());

        if (node != null)
        {

            XmlNodeList xnl = node.ChildNodes;
            for (int i = 0; i < xnl.Count;i++ )
            {//成员变量
                //将遍历出的值赋予数组

                xnl.Item(i).InnerText = OP.values[i];
            }
            xmlDoc.Save(PATH);
        }
    }
    public void Update<T>(T obj, string key)
    {
        //插入一个对象
        //对象有字段,属性值
        //利用数据的特点,即名称不同,优化查询速度,具体的做法为,将属性值作为子根操作。

        ObjectPara<T> OP = new ObjectPara<T>(obj);
        //

        XmlNode root = xmlDoc.SelectSingleNode(Myroot);//查找<game data>

        XmlNode Myroots = root.SelectSingleNode(OP.ObjName);//查询对象表

        XmlNode node = Myroots.SelectSingleNode(OP.GetKeyValue(ObjectID).ToString());

        if (node != null)
        {
            //找到名为name的对象
            XmlNode xl = node.SelectSingleNode(key);//找到字段
            xl.InnerText = OP.GetKeyValue(key).ToString();
            xmlDoc.Save(PATH);
        }
    }
    public void Delete<T>(T obj)
    {
        ObjectPara<T> OP = new ObjectPara<T>(obj);
        XmlNode root = xmlDoc.SelectSingleNode(Myroot);//查找<game data>

        XmlNode Myroots = root.SelectSingleNode(OP.ObjName);//查询对象表

        XmlNode node = Myroots.SelectSingleNode(OP.GetKeyValue(ObjectID).ToString());

        if (node != null)
        {
            node.RemoveAll();
            Myroots.RemoveChild(node);
            xmlDoc.Save(PATH);
        }
    }

    
}

因为xml的结构我们要动态生成的,不需要专门针对某一个类写操作语句。如何动态就靠解析类完成了。

好,下面是这个解析类,也就是用反射,本身并不难,关键是如何运用。特别声明的是,因为我操作的数据包含数组,而且数组只用到了字符数组,整型数组,以及浮点数组,所以只写了这三个数组的解析。其他类型数组你看需求自己定义:

using System;
using System.Reflection;
using UnityEngine;
using System.Collections.Generic;
using System.Runtime.InteropServices;
public class ObjectPara<T>
{
    public string ObjName;//待解析的类的名称
    public string[] cols;//类的成员变量
    public string[] values;//成员变量对应的值
    public PropertyInfo[] info;//反射获取类的属性
    public Type objtype;//待解析类型
    public T obj;//泛型对象
    public T GetObject(string[] values)
    {

        T _obj = obj;
        for (int i = 0; i < info.Length; i++)
        {
            if (objtype.GetProperty(cols[i]).PropertyType.IsArray)
            {
                
                Type type = objtype.GetProperty(cols[i]).PropertyType;
                string[] vale = values[i].Split(new char[] { ',' });
                if (type == typeof(string[]))
                {
                    info[i].SetValue(_obj, vale, null);
                }

                else if (type == typeof(int[]))
                {

                    int[] va = new int[vale.Length];
                    for (int j = 0; j < va.Length; j++)
                    {
                        va[j] = int.Parse(vale[j]);
                    }

                    info[i].SetValue(_obj, va, null);
                }
                else if (type == typeof(float[]))
                {
                    float[] va = new float[vale.Length];
                    for (int j = 0; j < va.Length; j++)
                    {
                        va[j] = float.Parse(vale[j]);
                    }

                    info[i].SetValue(_obj, va, null);
                }



                //MyTool.P(values[i]);
            }
            else
            {
                info[i].SetValue(_obj, Convert.ChangeType(values[i], info[i].PropertyType), null);
            }
        }
        return _obj;
    }
    public ObjectPara()
    {
    }
    public ObjectPara(T obj)
    {
        this.obj = obj;
        AnalyzeObject();
    }
    public void AnalyzeObject()//解析对象
    {
        //获取对象的名称,字段,以及值,以字符串数组的形式存储
        objtype = obj.GetType();
        ObjName = objtype.Name;
        info = objtype.GetProperties();
        cols = new string[info.Length];
        values = new string[info.Length];
        for (int i = 0; i < info.Length; i++)
        {
            cols[i] = info[i].Name;

            string value = "";
            if (objtype.GetProperty(cols[i]).PropertyType.IsArray)
            {

                if (objtype.GetProperty(cols[i]).GetValue(obj, null) != null)
                {

                    Type type = objtype.GetProperty(cols[i]).PropertyType;
                    if (type == typeof(string[]))
                    {
                        MyTool.P(value);
                        string[] ob = objtype.GetProperty(cols[i]).GetValue(obj, null) as string[];
                        if (ob != null)
                        {
                            foreach (var o in ob)
                            {
                                value += o + ",";

                            }
                            //value = value.Substring(0, value.Length-2);
                            value = value.TrimEnd(new char[] { ',' });
                        }
                    }

                    else if (type == typeof(int[]))
                    {
                        int[] ob = objtype.GetProperty(cols[i]).GetValue(obj, null) as int[];
                        if (ob != null)
                        {
                            foreach (var o in ob)
                            {
                                value += o + ",";

                            }
                            //value = value.Substring(0, value.Length-2);
                            value = value.TrimEnd(new char[] { ',' });
                        }
                    }
                    else if (type == typeof(float[]))
                    {
                        float[] ob = objtype.GetProperty(cols[i]).GetValue(obj, null) as float[];
                        if (ob != null)
                        {
                            foreach (var o in ob)
                            {
                                value += o + ",";

                            }
                            //value = value.Substring(0, value.Length-2);
                            value = value.TrimEnd(new char[] { ',' });
                        }
                    }
                }
            }
            else
            {
                value = Convert.ToString(objtype.GetProperty(cols[i]).GetValue(obj, null));
            }
            //MyTool.P(value);
            values[i] = value;
        }
    }
    public System.Object GetKeyValue(string col)
    {
        return (objtype.GetProperty(col).GetValue(obj, null));
    }
}

好了,以上就是设计万能xml的部分,如何使用呢?

很简单,你先写一个类,这个类很简单,或者说为了简单,只有一个成员变量。这样可以统一对所有对象进行操作,

public class GameData  {

    private string iD;

    public string ID
    {
        get { return iD; }
        set { iD = value; }
    }
}

然后随便写一个实体类,例如Person

public class MyPerson :GameData{

    private string name;

    public string Name
    {
        get { return name; }
        set { name = value; }
    }
    private int sort;

    public int Sort
    {
        get { return sort; }
        set { sort = value; }
    }
    private float life;

    public float Life
    {
        get { return life; }
        set { life = value; }
    }
}

接下来就是一个简单的测试类了,我就不传代码了。各位自己测试:

MyPerson p = new Myperson();

p.ID="lihua";

……

MyDataBase.Insert(p);添加

MyDataBase.Delete(p);删除

……

注意一下的问题,不要以为复制完代码就可以运行,主要是目录路径问题,如果你实在搞不定,直接先在Assent目录下建立一个路径DataBase,然后再建一个GameData.xml。好,接下来看你发挥。

 

后记:路径弄得有问题,建议不要使用上面提到的检测数据文件的方法,可以在进入游戏时检测并初始化数据库。相关的代码也很简单,主要是使用平台判断,www类,以及协程的使用。

using UnityEngine;
using System.Collections;
using System.IO;
public class CheckData : MonoBehaviour
{
    public string resData;
    public string gameData;
    public string path = "/GameData.xml";
    // Use this for initialization
    void Awake()
    {
        resData = Application.streamingAssetsPath + path;
#if UNITY_ANDROID
        gameData = Application.persistentDataPath+path;

#else
        gameData = Application.dataPath + path;
#endif
        StartCoroutine(ReadData(gameData));


    }
    IEnumerator ReadData(string path)
    {

        if (!File.Exists(path))
        {
#if UNITY_ANDROID
            WWW www = new WWW(resData);
            yield return www;
            if(www.isDone) {
               
                File.WriteAllBytes(path,www.bytes);
            }
#else
            File.Copy(resData, path);
#endif
            Application.LoadLevel(1);
        }
        else {
            Application.LoadLevel(1);
        }
        yield return 0;

    }


}

 

 

posted @ 2015-06-24 10:59  决晴谷  阅读(4191)  评论(0编辑  收藏  举报