Unity数据持久化_Json

数据持久化就是将内存中的数据模型转换为存储模型,以及将存储模型转换为内存中的数据模型的统称,简单来说就是将游戏数据从内存中存储到硬盘,或从硬盘中读取数据到内存

一、基础知识

1.1 Json文件格式

如何在Json文档中使用注释(注意在C#和Unity中默认不支持Json注释)

双斜杠 和 斜杠加星号包裹注释的Json文档格式,其后缀为.jsonc,不是 .json

在Json中实现一个实例对象,需要用大括弧包裹

如一个学生的实例:{"name":"小明","age":8,"isBoy":true}

在Json中实现一个数组,需要用中括弧包裹

如一个id数组:"ids": 1,2,3,4

在Json中如果存在字典,字典的键仍然要加双引号,其中字典可以当作一个类对象

如一个包含名字,学号的字典:{"name" : "XiaoMing", "Id" : "230901" }


练习题:使用Json语法描述 如下的类

复制代码
{
  "name" : "XiaoMing",
  "atk" : 12,
  "def" : 10,
  "moveSpeed": 4.5f,
  "roundSpeed": 3.1415926535,
  "weapon": {"id": 101, "num": 1 },
  "listInt": [1, 2, 3, 4, 5],
  "itemList": 
  [
    {"id": 201,"num": 10},
    {"id": 202,"num": 5}
  ],
  "itemDic": 
  {
    "1": {"id": 301, "num": 3},
    "2": {"id": 302,"num": 7}
  },
  "itemDic2": {
    "sword": {"id": 401,"num": 1},
    "shield": {"id": 402, "num": 1}
  }
}

对于非公有字段,序列化与反序列化时需要特性\[\],这里没有添加特性,所以为了避免反序列化出错, 不添加非公有字段对应的Json数据

1.2 C# 读取存储Json文件

1.2.1 JsonUtility

1.JsonUtility是Unity中用于解析Json文档的静态类

2.作用:讲内存中的数据对象序列化为Json格式的字符串或将Json字符串反序列化为类对象

3.对JSON字符串的存读:

将json字符串存到指定路径: File.WriteAllText(文件路径,Json字符串)

复制代码
 #region 将Json字符串存到指定路径
 // 语法:File.WriteAllText(文件路径,Json字符串)
 // 如 1.1中的Json文档存储到 persistentDataPath中
 string json = @"
 {
     ""name"" : ""XiaoMing"",
     ""atk"" : 12,
     ""def"" : 10,
     ""moveSpeed"": 4.5,
     ""roundSpeed"": 3.1415926535,
     ""weapon"": {""id"": 101, ""num"": 1 },
     ""listInt"": [1, 2, 3, 4, 5],
     ""itemList"": 
     [
         {""id"": 201,""num"": 10},
         {""id"": 202,""num"": 5}
     ],
     ""itemDic"": 
     {
         ""1"": {""id"": 301, ""num"": 3},
         ""2"": {""id"": 302,""num"": 7}
     },
     ""itemDic2"":   
     {
         ""sword"": {""id"": 401,""num"": 1},
         ""shield"": {""id"": 402, ""num"": 1}
     }
 }";
 File.WriteAllText(Application.persistentDataPath + "/Text.json",json);
 Debug.Log("保存成功!路径:" + Application.persistentDataPath + "/Text.json");
 EditorUtility.RevealInFinder(Application.persistentDataPath);

 // 注意:这个存储路径不会创建新文件夹,所以在存储时需要确保文件路径是否存在
 #endregion 从指定路径取出json字符串

运行的结果为

从指定路径取出json字符串: File.ReadAllTest(文件路径)

复制代码
 #region 从指定路径取出json字符串
 // 语法: File.ReadAllTest(文件路径)
 // 如将上面的Json字符串从指定文件路径中读取出来
 string jsonStr = File.ReadAllText(Application.persistentDataPath + "/Text.json");
 Debug.Log(jsonStr);

 #endregion

读取到的结果为:

4.使用JsonUtlity进行序列化(讲内存中的数据存到硬盘上)

方法: string jsonStr = JsonUtlity.ToJson(类对象实例)

缺点:字典和引用类型的null值无法存储,且float类型变量存储时会有误差

对于非公有字段和自定义类内部使用时,要添加特性System.Serializable才可以序列化与反序列化

复制代码
 #region  使用JsonUtlity进行序列化
 // 语法:JsonUtlity.ToJson(类对象实例),返回值为Json字符串
 #region 创建一个类对象实例
 PlayerInfo player = new PlayerInfo();

 player.name = "XiaoMing";
 player.atk = 12;
 player.def = 10;
 player.moveSpeed = 4.5f;
 player.roundSpeed = 3.1415926535;

 player.weapon = new Item(101, 1);

 player.listInt = new List<int>() { 1, 2, 3, 4, 5 };

 player.itemList = new List<Item>()
 {
     new Item(201, 10),
     new Item(202, 5)
 };

 player.itemDic = new Dictionary<int, Item>()
 {
     { 1, new Item(301, 3) },
     { 2, new Item(302, 7) }
 };

 player.itemDic2 = new Dictionary<string, Item>()
 {
     { "sword", new Item(401, 1) },
     { "shield", new Item(402, 1) }
 };
 #endregion
 string jsonStr2 = JsonUtility.ToJson(player);
 Debug.Log(jsonStr2);
 #endregion

运行结果为:

5.使用JsonUtlity进行反序列化(将硬盘中的数据读取到内存)

方法: JsonUtlity.FromJson(Json字符串,Type type)

使用:首先通过File.ReadzText(路径)读取json字符串,然后利用该方法进行反序列化

重载: JsonUtility.FromJson<目标类型>(Json字符串),通过泛型可以直接得到对应类的实例

注意:

  • 当json数据缺少时,并不影响反序列化的运行,这里会将其设置为默认值

  • 无法读取数据集合,需要将其包裹为对象,且文本格式为UTF-8

    #region 使用JsonUtlity进行反序列化
    // 语法:JsonUtlity.FromJson(Json字符串,Type type),返回值为Object类型
    // 重载:JsonUtility.FromJson<目标类型>(Json字符串)
    // 在使用前需要先得到Json字符串,可以通过File.ReadAllTest(文件路径) 从硬盘中读取
    PlayerInfo p1 = JsonUtility.FromJson(jsonStr2);
    PlayerInfo p2 = JsonUtility.FromJson(jsonStr2,typeof(PlayerInfo)) as PlayerInfo;

    #endregion

1.2.2ListJson

. 获取途径: LitJson官网:https://litjson.net/ => Source(这里点击后会调转GitHub) =>Releses => 下载压缩包 => 将压缩包解压,并将src中LitJson中的C#脚本导入到Unity,之后就可以正常使用LitJson

1.注意事项:

  • 不能序列化私有字段
  • 可以序列化字典,但因为在json中字典的键会加上双引号,所以当存在整形的key时,会被当做string序列化
  • LitJson可以保存null
  • 在反序列化时,要确保类中需要有无参构造函数
  • LitJson可以直接读取数据集合

2.使用LitJson进行序列化:

方法:JsonMapper.ToJson(类对象实例),返回值为json字符串

复制代码
#region 使用ListJson进行序列化
// 语法:JsonMapper.ToJson(类对象实例),返回值为Json字符串
// 如,将PlayerInfo的实例对象player进行序列化得到playerJson
string playerJson = JsonMapper.ToJson(player);
Debug.Log("通过 { JsonMapper.ToJson(player) } 得到的结果为 " + playerJson);
#endregion

3.使用LitJson进行反序列化:

方法:JsonMapper.ToObject(json字符串),返回值为JsonData类对象

JsonDat是LitJson提供的类对象,可以用键值对的形式访问其中的内容

复制代码
 #region 使用LitJson进行反序列化
 // 语法:JsonMapper.ToObject(Json字符串),返回值为JsonData类对象
 JsonData jsonData = JsonMapper.ToObject(playerJson);
 // 这里可以直接通过键来得到其中的信息,如获取玩家信息的名字
 string pName = jsonData["name"].ToString();
 // 还原为原本的类对象:通过泛型 JsonMapper.ToObject<泛型>(Json字符串)
 PlayerInfo p4 = JsonMapper.ToObject<PlayerInfo>(playerJson);
 #endregion

1.2.3 JsonUtility和LitJson对比

1.相同点:

  • 都是用于json进行序列化和反序列化的静态类工具
  • 处理的json文档格式必须是UTF-8

2.不同点:

  • JsonUtlity是Unity自带的静态工具类,LitJson是第三方需要引用命名空间
  • JsonUtlity使用自定义类需要添加特性,LitJson不需要特性可以直接序列化
  • jsonUtlity可以序列化非公有字段但要添加特性,LitJson不需要也不支持非公有字段序列化
  • JsonUtility不支持字典,LitJson支持但键只能是字符串
  • JsonUtility不能直接将数据反序列化为数据集合(数组字典),LitJson可以
  • JsonUtility对自定义类不要求有无参构造,LitJson需要
  • JsonUtility存储空对象时会存储默认值而不是null,LitJson会存null

二、实践内容

1.定义 Json 序列化 / 反序列化类型枚举JsonType,包含JsonUtlityLitJson两种方式,用于切换序列化工具

复制代码
public enum JsonType
{
    JsonUtlity,
    LitJson,
}

2.构建JsonMgr数据管理类,采用单例模式设计,保证全局唯一实例,方便外部统一调用

3.实现SaveData存储方法:

  • 确定数据存储路径为Application.persistentDataPath

  • 根据传入的JsonType选择对应工具,将内存中的对象序列化为 Json 字符串

  • 通过File.WriteAllText将 Json 字符串写入硬盘指定路径

    //存储Json数据进行序列化 => 将数据从内存中存入硬盘
    public void SaveData(object data, string fileName, JsonType type = JsonType.LitJson)
    {
    //存储的路径
    string path = Application.persistentDataPath + "/" + fileName + ".json";
    string jsonStr = ""; //json字符串
    switch (type)
    {
    case JsonType.JsonUtlity:
    jsonStr = JsonUtility.ToJson(data);
    break;
    case JsonType.LitJson:
    jsonStr = JsonMapper.ToJson(data);
    break;
    }
    //将Json字符串存储到指定路径中
    File.WriteAllText(path, jsonStr);
    }

4.实现LoadData读取方法:

  • 优先从Application.streamingAssetsPath路径查找目标 Json 文件

  • 若路径不存在,切换到Application.persistentDataPath路径二次查找

  • 若两个路径均无文件,返回对应类型的默认实例对象

  • 若存在文件,读取 Json 字符串,根据JsonType选择对应工具反序列化为对象并返回

    public T LoadData(string fileName, JsonType type = JsonType.LitJson) where T : new()
    {
    //读取的路径
    string path = Application.streamingAssetsPath + "/" + fileName + ".json";

    复制代码
      // 这里如果读取的是两个不同类型的,如配置表 和玩家存档
      // 配置表一般放在streamingAssets中
      // 玩家存档放在 persistentDataPath
      // 所以这里要是在streamingAssets中找不到,就到persistentDataPath中找
      if (!File.Exists(path))
      {
          path = Application.persistentDataPath + "/" + fileName + ".json";
      }
    
      //先在指定路径读取json字符串
      if (!File.Exists(path))
          return new T();//如果没有在指定路径找到就直接返回默认的实例对象
      //通过json字符串进行反序列化
      string jsonStr = File.ReadAllText(path);
      T data = default(T);
      switch (type)
      {
          case JsonType.JsonUtlity:
              data = JsonUtility.FromJson<T>(jsonStr);
              break;
          case JsonType.LitJson:
              data = JsonMapper.ToObject<T>(jsonStr);
              break;
      }
      //将得到的实例对象返回
      return data;

    }

5.进行打包,这里我们写了JsonMgr脚本,但其运行时需要依赖LitJson相关脚本,所以需要一起打包

相关推荐
.千余2 小时前
【C++】C++核心语法:函数重载与缺省参数原理与避坑
c语言·开发语言·c++·经验分享·笔记·git·学习
heimeiyingwang2 小时前
【架构实战】订单系统架构设计:电商核心系统的演进
unity·架构·系统架构
段一凡-华北理工大学2 小时前
工业领域的Hadoop架构学习~系列文章03:MapReduce编程模型深度解读
大数据·人工智能·hadoop·学习·架构·高炉炼铁·高炉智能化
元气少女小圆丶2 小时前
SenseGlove Nova 2+Unity开发笔记3
笔记·unity·游戏引擎
bush42 小时前
嵌入式linux学习记录三
学习
霸道流氓气质2 小时前
Spring AI Alibaba 学习路线图:从入门到精通
人工智能·学习·spring
Engineer邓祥浩2 小时前
宏观认知(二):AI项目落地与团队协作——吴恩达《AI for Everyone》Week2学习笔记
人工智能·笔记·学习
WMX10122 小时前
Unity-shader学习记录
学习·unity·游戏引擎
Mister西泽2 小时前
线性代数-学习日记
学习