在游戏中Xml文件是经常会用来存储游戏信息或者是存储游戏数据,那么我们就需要对其Xml文件进行创建、保存、读取、删除等操作。这里简单介绍在Unity下怎样多Xml文件进行操作。
Xml文件的操作是.net标准模板库里面的基本功能,因此需要引入这个模板库才能进行操作,如下所示:
代码如下所示:
public static void CreateXml(string path)
{
XmlDocument xmlDoc = new XmlDocument();
////创建类型声明节点
try
{
xmlDoc.Save(path);
}
catch (Exception e)
{
//显示错误信息
Debug.Log(e.Message);
}
}
}
这里只需要传入你想将数据保存到的path路径位置即可,这样你就可以创建一个简单到Xml文件即可,但是这样创建有个xml有个问题,如果文件夹中有个文件就会报错。
那么为了避免这个问题要怎么修改代码呢?
这里我就需要引入另一个标准模板库,进行文件查找和检查重复的问题。如下所示:
代码如下所示:
public static void CreateXml(string path)
{
XmlDocument xmlDoc = new XmlDocument();
///检查文件是否存在
if (File.Exists(path))
{
Console.WriteLine("该文件已存在!!");
Debug.Log("该文件已存在!!");
return;
}
else //不存在就创建
{
try
{
xmlDoc.Save(path);
}
catch (Exception e)
{
//显示错误信息
Debug.Log(e.Message);
}
}
}
这样就可以避免了出现问题了,提高了代码的健壮性。然后测试下代码,如下所示:
这里我就简单的用UI去测试在文件夹下创建一个test3.xml文件,测试结果如下所示:
打开test3.xml文件会发现这个文件是空的并没有任何的数据在里面,这样不利于区别这个文件究竟有没有使用过或者是使用情况,我希望创建的时候xml文件就具有简单的数据格式。那么需要再次改写代码,代码如下
public static void CreateXml(string path, StringBuilder data = null)
{
XmlDocument xmlDoc = new XmlDocument();
////创建类型声明节点
XmlNode node = xmlDoc.CreateXmlDeclaration("1.0", "utf-8", "");
xmlDoc.AppendChild(node);
//创建根节点
XmlNode root = xmlDoc.CreateElement("User");
xmlDoc.AppendChild(root);
CreateNode(xmlDoc, root, "path", "xuwei");
CreateNode(xmlDoc, root, "sex", "male");
CreateNode(xmlDoc, root, "age", "25");
///检查文件是否存在
if (File.Exists(path))
{
Console.WriteLine("该文件已存在!!");
Debug.Log("该文件已存在!!");
return;
}
else //不存在就创建
{
try
{
xmlDoc.Save(path);
}
catch (Exception e)
{
//显示错误信息
Debug.Log(e.Message);
}
}
}
添加节点的代码:
private static void CreateNode(XmlDocument xmlDoc, XmlNode parentNode, string name, string value)
{
XmlNode node = xmlDoc.CreateNode(XmlNodeType.Element, name, null);
node.InnerText = value;
parentNode.AppendChild(node);
}
代码已经写好了来测试一下功能,这次我把数据信息存放到test4.xml文件里面查看,测试效果如下所示:
如果使用一段时间会觉得这样的函数非常繁琐,如果数据结构不一样就需要改写这个函数这样非常麻烦的做法,有没有比较方便的方式。根据这个需求继续修改这个函数,我们在往这个类添加一个标准模板库,如下所示:
然后将代码改写成最后的效果,代码如下:
/// <summary>
/// 创建一个空的xml文件
/// </summary>
/// <param path="path">xml文件名</param>
/// <param StringBuilder="data">添加数据</param>
public static void CreateXml(string path, StringBuilder data = null)
{
XmlDocument xmlDoc = new XmlDocument();
////创建类型声明节点
XmlNode node = xmlDoc.CreateXmlDeclaration("1.0", "utf-8", "");
///检查文件是否存在
if (File.Exists(path))
{
Console.WriteLine("该文件已存在!!");
Debug.Log("该文件已存在!!");
return;
}
else //不存在就创建
{
try
{
xmlDoc.Save(path);
if (data != null)
{
string t = data.ToString();
StreamWriter sw = new StreamWriter(path, false, Encoding.UTF8);
sw.Write(t);
sw.Flush();
sw.Dispose();
}
}
catch (Exception e)
{
//显示错误信息
Debug.Log(e.Message);
}
}
}
最后的测试代码如下:
public void OnClickCreateButton()
{
string path = String.Format(Consts.DataTestDir + "{0}.xml", "test");
Tools.CreateXml(path, CreateWriteXml("Begonia", "22324", "1890685", "25"));
}
测试效果效果如下所示:
创建了xml文件并不是最后的效果,我们还要进行数据的保存这个数据,保存数据的代码比较简单,我直接将用注释来解释即可,代码如下:
/// <summary>
/// 保存数据文件
/// </summary>
/// <param name="fileName">文件路径</param>
/// <param name="sb">保存的数据排版</param>
public static void SaveXml(string fileName, StringBuilder sb)
{
//检查文件是否存在
if(!File.Exists(fileName))
return;
//数据转化为字符串
string t = sb.ToString();
//找到xml文件
StreamWriter sw = new StreamWriter(fileName, false, Encoding.UTF8);
sw.Write(t);//写入数据
//保存并且关闭xml文件
sw.Flush();
sw.Dispose();
}
那么最后是数据操作的介绍了,读取数据方法,方法代码如下:
private void LoadXml(string fileName, ref Player player)
{
//获取xml路径下的文件
FileInfo file = new FileInfo(fileName);
StreamReader sr = new StreamReader(file.OpenRead(), Encoding.UTF8);
//打开并且读取xml文件
XmlDocument doc = new XmlDocument();
doc.Load(sr);
player.Name = doc.SelectSingleNode("/Level/Name").InnerText;
player.QQ = int.Parse(doc.SelectSingleNode("/Level/QQ").InnerText);
player.Phone = doc.SelectSingleNode("/Level/Phone").InnerText;
player.Year = int.Parse(doc.SelectSingleNode("/Level/Year").InnerText);
//关闭xml文件并且释放资源
sr.Close();
sr.Dispose();
}
这样不难发现,因为xml数据格式都是自定义的,所以会有很多层级关系,这样一来我们就要写很多个LoadXml方法如此一来。除去数据以外其他的代码都是一样的,这样我们可与不可以将其抽取出来单独形成一个函数方法呢?答案是可以的。
实现这个函数需要用到委托进行(PS:不懂委托方法的小朋友自行谷歌度娘),每次调用函数的时候就需要提供数据提取方法。最后实现的代码如下所示:
/// <summary>
/// 读取xml的数据
/// </summary>
/// <typeparam name="T">泛型</typeparam>
/// <param name="fileName">xml的路径</param>
/// <param name="rm">读取的数据的方法</param>
/// <param name="data">读取数据</param>
public static void LoadDataFromXml<T>(string fileName, ReadMethod<T> rm, ref T data)
{
//查找xml文件是否存在
if(!File.Exists(fileName))
return;
//获取xml路径下的文件
FileInfo file = new FileInfo(fileName);
StreamReader sr = new StreamReader(file.OpenRead(), Encoding.UTF8);
//委托方法
rm(sr, ref data);
//关闭xml文件并且释放资源
sr.Close();
sr.Dispose();
}
这里使用泛型和委托两种方式共同实现了读取数据这块的功能。然后进行简单测试,测试代码如下:
最后在Unity下测试的效果如下图所示:
最后将这个方法统一放到Tools工具类函数里面进行统一管理,代码如下所示:
using System;
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Runtime.CompilerServices;
using System.Text;
using System.Xml;
public static class Tools
{
public delegate void ReadMethod<T>(StreamReader sr, ref T type);
/// <summary>
/// 获取Path目录下的所有文件
/// </summary>
/// <param path="path"></param>
/// <param path="type"></param>
/// <returns></returns>
public static List<FileInfo> GetAllFile(string path, string type)
{
string[] files = Directory.GetFiles(path, type);
List<FileInfo> list = new List<FileInfo>();
for (int i = 0; i < files.Length; i++)
{
FileInfo file = new FileInfo(files[i]);
list.Add(file);
}
return list;
}
/// <summary>
/// 创建一个空的xml文件
/// </summary>
/// <param path="path">xml文件名</param>
/// <param StringBuilder="data">添加数据</param>
public static void CreateNewXml(string path, StringBuilder data = null)
{
XmlDocument xmlDoc = new XmlDocument();
////创建类型声明节点
XmlNode node = xmlDoc.CreateXmlDeclaration("1.0", "utf-8", "");
///检查文件是否存在
if (File.Exists(path))
{
Console.WriteLine("该文件已存在!!");
Debug.Log("该文件已存在!!");
return;
}
else //不存在就创建
{
try
{
xmlDoc.Save(path);
if (data != null)
{
string t = data.ToString();
StreamWriter sw = new StreamWriter(path, false, Encoding.UTF8);
sw.Write(t);
sw.Flush();
sw.Dispose();
}
}
catch (Exception e)
{
//显示错误信息
Debug.Log(e.Message);
}
}
}
/// <summary>
/// 保存数据文件
/// </summary>
/// <param name="fileName">文件路径</param>
/// <param name="data">保存的数据排版</param>
public static void SaveDateToXml(string fileName, StringBuilder data)
{
//检查文件是否存在
if(!File.Exists(fileName))
return;
//数据转化为字符串
string t = data.ToString();
//找到xml文件
StreamWriter sw = new StreamWriter(fileName, false, Encoding.UTF8);
sw.Write(t);//写入数据
//保存并且关闭xml文件
sw.Flush();
sw.Dispose();
}
/// <summary>
/// 读取xml的数据
/// </summary>
/// <typeparam name="T">泛型</typeparam>
/// <param name="fileName">xml的路径</param>
/// <param name="rm">读取的数据的方法</param>
/// <param name="data">读取数据</param>
public static void LoadDataFromXml<T>(string fileName, ReadMethod<T> rm, ref T data)
{
//查找xml文件是否存在
if(!File.Exists(fileName))
return;
//获取xml路径下的文件
FileInfo file = new FileInfo(fileName);
StreamReader sr = new StreamReader(file.OpenRead(), Encoding.UTF8);
//委托方法
rm(sr, ref data);
//关闭xml文件并且释放资源
sr.Close();
sr.Dispose();
}
}