Unity xml反序列化为数据类
发表于2018-08-01
xml在unity中,经常用来保存本地数据,我们在处理xml文件的时候,通常是先创建一个xml对应的数据类,然后将xml解析的每一个element封装成对象,在保存的list或者diction集合中。解析xml的过程大部分都是相同的,但是不同的数据类,对应的字段,相差较大,所以之前在解析的时候,都是单独解析的。
最近闲来无事,就讲xml解析的方法进行了加工,利用c#的反射技术和泛型的特点,创建一个xml解析工具,这样,我们只用传递xml文件以及生产文件的数据类,就可以轻松搞定繁琐的xml转换了。
首先我们需要一个xml文件,手写xml文件太费劲了,我们可以先在Excel中创建数据,保存为CSV格式的文件,然后在写个工具,把csv文件转换为xml文件(虽然这样也有点麻烦,但是感觉比直接写xml要好一点)
直接上代码 :
using UnityEngine; using System.Xml; using System.IO; using UnityEditor; //using System; /// <summary> /// 把给定列数的csv文件转换为xml文件.该类文件放在file/csv文件夹下 /// csv文件的格式:第一行为属性的名称,后面若干行为属性的值 /// 生成的xml文件,放在StreamingAssets/xml目录下 /// </summary> public class CsvToXmlTools : Editor { [MenuItem("Assets/CsvToXml/Story")] static void CsvToXml_Story(){ string path=AssetDatabase.GetAssetPath (Selection.activeObject); if(path.EndsWith(".csv")) loadFile (path); } /// <summary> /// 加载csv文件,并按行读取数据. /// </summary> /// <param name="filePath">文件路径.</param> /// <param name="columCount">列数.</param> static void loadFile(string filePath){ string path = filePath; string saveName = Path.GetFileNameWithoutExtension(path); string xmlPath = Application.streamingAssetsPath + "/xml/" +saveName+ ".xml"; CreatXmlFile (xmlPath);//创建或者覆盖之前的xml文件 //读取csv文件 FileStream fs = new FileStream (path,FileMode.Open); using(StreamReader sr=new StreamReader(fs)){ string str = sr.ReadLine (); string[] keys = str.Split (',');//保存属性名称 // str = sr.ReadLine (); // addToXml (new string[]{"dataType"},new string[]{str},xmlPath);//保存数据格式 int columCount = keys.Length ; while ((str=sr.ReadLine ())!=null) { string[] arr = str.Split (','); if (keys.Length == arr.Length) { addToXml (keys,arr,xmlPath);//保存读取的数据到xml文件中 } else Debug.LogError ("data error"+str); } } fs.Close (); Debug.Log ("转换文件结束"); } /// <summary> /// 为xml文件添加新的节点. /// </summary> /// <param name="keys">需要添加的数据的健.</param> /// <param name="arr">需要添加的数据值.</param> /// <param name="xmlPath">xml文件的位置.</param> static void addToXml(string[] keys,string[] values,string xmlPath){ XmlDocument xmldoc = new XmlDocument (); xmldoc.Load (xmlPath); XmlNode root= xmldoc.SelectSingleNode ("root"); XmlElement record = xmldoc.CreateElement ("Record"); for (int i = 0; i < keys.Length; i++) { // Debug.Log(keys[i]+"-------"+values[i]); record.SetAttribute (keys [i], values [i]); } root.AppendChild (record); xmldoc.Save (xmlPath); } /// <summary> /// 创建一个新的xml文件. /// </summary> /// <param name="path">xml文件的保存路径.</param> static void CreatXmlFile(string path){ XmlDocument doc = new XmlDocument (); XmlDeclaration dec= doc.CreateXmlDeclaration("1.0", "utf-8", null); doc.AppendChild (dec); XmlNode root= doc.CreateElement ("root"); doc.AppendChild (root); doc.Save (path); Debug.Log ("save path=" + path); } }
这是我们的编辑器工具,选择我们的csv文件,右键,选CsvToXml/Story,即可,生成的xml文件自动保存在StreamingAssets/xml/目录下,没有该目录的需要自己手动创建一下,代码里就懒得做处理了。
然后就是我们的xml解析类,我们把xml解析放在基类里面,子类只用继承调用即可。
每一个xml文件,会有一个单独的数据类和它对应,每一个字段对应一个属性。
首先是我们的xml文件
<?xml version="1.0" encoding="utf-8"?> <root> <Record id="1" chapter="1" index="1" content="你是故意与我争斗,以骗取启魂邪教的信任?" hero="aa" heroName="洛昭言" type="true" /> <Record id="2" chapter="1" index="2" content="没错。不瞒兄弟,我们两人昨天刚到盈辉堡,这丫头就上了刚才那家伙的当,把钱全给了那个什么圣宗。没办法,我只好装傻跟那个教使套近乎,看能不能想法把钱弄回来" hero="bb" heroName="越今朝" type="false" /> <Record id="3" chapter="1" index="3" content="我没上当,以前就见过穿这种衣服的骗子啊。可是你说过,要是别人都去做一件事,我最好也跟着做。昨天好多人都把钱给他了啊。" hero="aa" heroName="祈" type="true" /> <Record id="4" chapter="1" index="4" content="我教过你明知是坑还往里跳吗?" hero="aa" heroName="越今朝" type="true" /> <Record id="5" chapter="1" index="5" content="我教过你明知是坑还往里跳吗?" hero="bb" heroName="祈" type="false" /> <Record id="6" chapter="2" index="1" content="我一定是上辈子欠你的…… " hero="aa" heroName="越今朝" type="true" /> <Record id="7" chapter="2" index="2" content="加斯递交的年代" hero="bb" heroName="洛昭言" type="false" /> <Record id="8" chapter="2" index="3" content=":那个……两位……" hero="aa" heroName="aa" type="true" /> <Record id="9" chapter="2" index="4" content="呃……咳咳。我叫越今朝,这是越祈,兄弟怎么称呼?" hero="bb" heroName="越今朝" type="false" /> <Record id="10" chapter="2" index="5" content="洛昭言" hero="aa" heroName="洛昭言" type="true" /> <Record id="11" chapter="2" index="6" content="难道您就是这一带有名的昙华洛家的家主?" hero="bb" heroName="越今朝" type="false" /> </root>
然后使我们的数据基类
public class ModelBase { public int id; }
数据类
public class StoryEntry:ModelBase{ public string chapter; public string index; public string content; public string hero; public string heroName; public bool type; // public void show(){ // string str = ""; // //取得当前方法命名空间 // str += "命名空间名:" + System.Reflection.MethodBase.GetCurrentMethod().DeclaringType.Namespace + "\n"; // //取得当前方法类全名 // str += "类名:" + System.Reflection.MethodBase.GetCurrentMethod().DeclaringType.FullName + "\n"; // Debug.Log (str); // } }
xml解析的基类
using System.Collections; using System.Collections.Generic; using System.Xml; using System.Reflection; public class ModelMgrBase { public Dictionary<int,ModelBase> dataDic = new Dictionary<int, ModelBase>(); public virtual void Init(){ } /// <summary> /// 从 xml 中加载数据并保存到字典里面. /// </summary> /// <param name="path">Path.</param> /// <param name="keys">Keys.</param> /// <param name="className">Class name.</param> protected void LoadFromXml(string path,string[] keys,string className,string[] dataTypes){ XmlDocument xml = new XmlDocument (); xml.Load (path); XmlNode root= xml.SelectSingleNode ("root"); XmlNodeList nodes = root.ChildNodes; // string[] dataTypes=GetDataType ((XmlElement)nodes [0]); for (int i = 1; i < nodes.Count; i++) { XmlElement item = nodes[i] as XmlElement; Assembly assembly = Assembly.GetExecutingAssembly();//获取命名空间 ModelBase model= (ModelBase)assembly.CreateInstance (className);//实例化对象 if (model == null) continue; System.Type type= model.GetType (); for (int j = 0; j < keys.Length; j++) { FieldInfo fi= type.GetField (keys [j]); object dataValue=null; switch (dataTypes[j]) { case "int": dataValue = int.Parse (item.GetAttribute (keys [j])); break; case "float": dataValue = float.Parse (item.GetAttribute (keys [j])); break; case "bool": dataValue = bool.Parse (item.GetAttribute (keys [j])); break; case "string": dataValue =item.GetAttribute (keys [j]); break; default: break; } if(fi!=null){ fi.SetValue(model,dataValue); } } dataDic.Add (model.id,model); } } // string[] GetDataType(XmlElement element){ //// Debug.Log (bool.Parse("true")); //// Debug.Log (float.Parse("1.23")); // string dt = element.GetAttribute ("dataType"); // return dt.Split(','); // } /// <summary> /// 通过ID从集合中查找数据. /// </summary> /// <param name="id">Identifier.</param> /// <typeparam name="T">The 1st type parameter.</typeparam> public T GetDataById<T>(int id) where T:ModelBase{ T model; ModelBase mb; dataDic.TryGetValue (id, out mb); if(dataDic.ContainsKey(id)){ return (T)dataDic[id]; }else return default(T); } /// <summary> /// 通过className创建实例对象. /// </summary> /// <returns>The instance by class name.</returns> /// <param name="className">Class name.</param> /// <typeparam name="T">The 1st type parameter.</typeparam> private T CreateInstanceByClassName<T>(string className){ Assembly assembly = Assembly.GetExecutingAssembly(); object obj= assembly.CreateInstance (className); // obj.GetType().GetField("chapter").SetValue(obj,21); //获取指定名称的字段 // System.Reflection.PropertyInfo propertyInfo = type.GetProperty("chapter"); //获取指定名称的属性 // propertyInfo.SetValue(obj,21,null); return (T)obj; } }
xml数据管理类
using UnityEngine; using System.Collections; using System.Collections.Generic; using System.Xml; using System.Reflection; public class StoryMgr:ModelMgrBase { public class StoryEntry:ModelBase{ public string chapter; public string index; public string content; public string hero; public string heroName; public bool type; public void show(){ string str = ""; //取得当前方法命名空间 str += "命名空间名:" + System.Reflection.MethodBase.GetCurrentMethod().DeclaringType.Namespace + "\n"; //取得当前方法类全名 str += "类名:" + System.Reflection.MethodBase.GetCurrentMethod().DeclaringType.FullName + "\n"; Debug.Log (str); } } private static StoryMgr instance; public static StoryMgr Instance{ get { if (instance == null) instance = new StoryMgr (); return instance; } } /// <summary> /// 初始化数据,然后假装xml,保存到字典集合中. /// </summary> public override void Init () { base.Init (); string path=Application.streamingAssetsPath+"/xml/story.xml"; string[] keys = new string[]{ "id","chapter","index","content","hero","heroName","type"};// xml 的属性名称 string[] dataTypes = new string[]{"int","string","string","string","string","string","bool" };//对应的数据类型 string className = "StoryMgr+StoryEntry"; //classname=命名空间+“.”+类全名,如果命名空间为空,只用添加类全名 LoadFromXml (path,keys,className,dataTypes); } }
最后,创建一个mono脚本,测试一下我们的方法
using UnityEngine; using System.Collections; public class testScript : MonoBehaviour { // Use this for initialization void Start () { StoryMgr.Instance.Init (); StoryMgr.StoryEntry st=StoryMgr.Instance.GetDataById<StoryMgr.StoryEntry> (3); Debug.Log (st.id+"--"+st.content+"--"+st.type); } // Update is called once per frame void Update () { } }
以后再解析其他xml的时候,只要创建好数据类,然后xml管理类直接继承xml解析类,就可以将xml的数据保存到数据字典里面。
来自:https://blog.csdn.net/u011484013/article/details/73920806