最近项目中需要借助txt保存一些文件的数据,于是学习了使用json
总的来说非常方便,易于理解
需求与分析
需要能够将生成的文件在本地保存一年(自创建时起),期间可以随时提取并使用
要有两种搜索途径,一种是通过输入完整的id码(由创建时的时间和随机数组成),另一种是通过查看某一天的全部文件自行筛选
由于读取txt文档比逐个读取文件并判断文件名更有效率,考虑创建文件时根据日期来存入对应的文件夹(File - 年 - 月 - 日),并将创建日期、地址数据封装成一个FileData类存入当天的Json文件FileDataDaily类,这样可以先把搜索范围精确到某一天,再进行剩下的筛选
再将该FileDataDaily文件的创建日期和地址封装成一个FDDInfo类,根据日期查询到该FDDInfo文件的地址,来读取该FileDataDaily文件,获取当天的所有文件的FileData
也可以更细分为每个月、每年的FileInfo文件,这里因为只保存1年,并没有分的太细
代码摘录和笔记
Json和txt文件
Json部分可以使用unity自带的JsonUtility,也可以使用Newtonsoft.Json
使用Json将类的实例的数据存入txt文件 = 将该实例序列化后写入txt文件
使用Json从txt读取类的实例的数据 = 读取txt文件后反序列化生成该实例
可序列化的属性可以在类的开头使用[Serializable]
来声明
// 每个文件的信息
[System.Serializable]
public class FileData
{
public string id;
public string path;
}
// 每日生成的所有文件的链表
[System.Serializable]
public class FileDataDaily
{
public List<FileData> FileDataList;
}
// 每日生成的FileDataDaily文件的信息
[System.Serializable]
public class FDDInfo
{
// json文件生成的年月日
public int year;
public int month;
public int day;
// json文件存储的地址
public string path;
}
// 每年生成的FileDataDaily文件的链表
[System.Serializable]
public class FDDInfoList
{
public List<FDDInfo> infoList;
}
类之间的关系如下:
其中FDDInfoList和FileDataDaily会作为文件保存,内容分别为FDDInfo和FileDataList序列化的链表
工具脚本
工具脚本要有读取、写入txt的功能,而能正确读取和写入txt,需要可以进行正确的序列化和反序列化
序列化与反序列化
public string ToJson(object o)
{
if (o != null)
{
return JsonConvert.SerializeObject(o);
}
return null;
}
public T ToClass<T>(string json)
{
try
{
return JsonConvert.DeserializeObject<T>(json);
}
catch (Exception ex)
{
Debug.LogError("JsonErroe:" + ex);
Debug.LogError("JsonErroe:" + json);
return default(T);
}
}
将Json数据写入txt文件
需要能够创建新的文件
public void Write(string text, string _path, out bool needCreate)
{
needCreate = false;
if (!File.Exists(_path))
{
// 直接创建txt文件会因为无权限而报错
//sr = File.CreateText(_path);
//sr.Flush();
//sr.Close();
//sr = null;
// 改用复制一个txt文件即可
File.Copy(path, _path);
needCreate = true;
}
if (File.Exists(_path))
{
StreamWriter sr = null;
// 使用AppendAllText而不是WriteAllText来指定编码模式,防止写入时覆盖
File.AppendAllText(path, string.Empty, Encoding.UTF8);
FileStream fs = new FileStream(_path, FileMode.Append);
if (sr == null)
{
sr = new StreamWriter(fs);
}
// 转换成json可以读取的数据结构
text = text.Insert(0, ",");
Debug.Log("Write json: " + text);
sr.Write(text);
sr.Flush();
sr.Close();
fs.Close();
}
}
在第一次写入文件时需要先创建(复制)一个文件,且第一次会比较特殊,可能会希望做些什么,所以返回一个needCreate值备用
读取txt中的Json数据
public string Read(string _path, int type)
{
string text = null;
if (File.Exists(_path))
{
text = File.ReadAllText(_path, Encoding.UTF8);
// 转换成json可以读取的数据结构,添加文件头部的标识信息
switch (type)
{
case 1:
{
text = text.Insert(0, "{\"pathList\":[");
text += "]}";
}break;
case 2:
{
text = text.Insert(0, "{\"FileDataList\":[");
text += "]}";
}
break;
}
}
Debug.Log("Read json: " + text);
return text;
}
因序列化并写入时是逐次对单个实例进行操作,而读取时是默认对读取了全部的数据(即读取了实例的链表)再进行反序列化,所以需要根据想要反序列化的类型来添加文件头部的标识信息以及尾部的结束标识,以及实例数据之间的分隔符
实例数据的分隔符分布在各个实例数据之间,因此可以在写入文件时加入
标识信息位于整个txt数据的头部和尾部,因此可以在读取文件时加入
关于string.Insert()
Insert会创建一个新字符串,不会覆盖原来的字符串,所以直接调用是看不出效果的
关于字符串中加入引号
string str = "abc\"\""; // str=abc""
获取某日的全部文件
输出数据可以作为模糊搜索的结果,也可以继续使用id进行精确筛选
public FileDataDaily GetFileInfo(int mounth, int day)
{
FileDataDaily info = new FileDataDaily();
for (int i = 0; i < response.infoList.Count; i++)
{
FDDInfo resp = response.infoList[i];
if (resp.month == mounth && resp.day == day)
{
string tempPath = resp.path;
string josn = Read(tempPath, 2);
info = ToClass<FileDataDaily>(josn);
break;
}
}
return info;
}
使用的时候,先将类用ToJson转换成json的string,再调用Write写入;先将txt中的string读取,再调用ToClass转换成类即可
JsonTool jsonTool = JsonTool.Instance;
string fileDataJson = jsonTool.ToJson(fileData);
bool isNewDay;
jsonTool.Write(fileDataJson, fddInfoPath, out isNewDay);
if (isNewDay) {
// ...
}
string fileDataListStr = jsonTool.Read(fddInfoPath, 2);
FileDataDaily fileDataDaily = jsonTool.ToClass<FileDataDaily>(fileDataListStr);
参考文档
unity JsonUtility泛型缺陷与Newtonsoft.Json
C#中File和FileStream的用法
关于Newtonsoft Json中的集合数据转换