Unity_数据持久化_Json

Unity_数据持久化

四、Json数据持久化

4.1 Json基本语法

4.1.1 基本概念

什么是JSON:

JSON(JavaScript Object Notation)是一种轻量级的数据交换格式,具有以下特点:

  • 轻量级:相比XML,JSON格式更简洁,数据量更小
  • 可读性:人类和机器都能轻松理解
  • 跨平台:不依赖特定编程语言,支持多种平台
  • 自描述性:数据结构清晰,易于解析

JSON在Unity中的应用:

  • 游戏配置数据存储
  • 网络通信数据格式
  • 存档文件格式
  • 第三方API数据交换
4.1.2 数据类型

JSON支持六种基本数据类型:

1. 字符串(String)

json 复制代码
"Hello World"
"Unity游戏开发"
"12345"

2. 数字(Number)

json 复制代码
42          // 整数
3.14        // 浮点数
-273        // 负数
1.23e-4     // 科学计数法

3. 布尔值(Boolean)

json 复制代码
true        // 真
false       // 假

4. 空值(Null)

json 复制代码
null        // 表示空值或未定义

5. 数组(Array)

json 复制代码
[1, 2, 3, 4, 5]                    // 数字数组
["苹果", "香蕉", "橙子"]            // 字符串数组
[true, false, true]                 // 布尔值数组
[1, "混合", true, null]            // 混合类型数组

6. 对象(Object)

json 复制代码
{
    "name": "张三",
    "age": 25,
    "isStudent": true
}
4.1.3 语法规则

基本语法规则:

1. 键值对格式

json 复制代码
{
    "键名": "值"
}

2. 命名规则

  • 键名必须用双引号包围
  • 键名区分大小写
  • 键名不能重复
  • 支持中文和特殊字符

3. 值类型规则

  • 字符串必须用双引号包围
  • 数字不需要引号
  • 布尔值和null不需要引号
  • 数组和对象不需要引号

4. 嵌套规则

json 复制代码
{
    "player": {
        "name": "张三",
        "stats": {
            "health": 100,
            "mana": 50
        },
        "inventory": [
            {"id": 1, "name": "剑"},
            {"id": 2, "name": "盾"}
        ]
    }
}

5. 分隔符规则

  • 键值对之间用逗号分隔
  • 数组元素之间用逗号分隔
  • 最后一个元素后不能有逗号
4.1.4 格式示例

游戏数据JSON示例:

1. 玩家信息

json 复制代码
{
    "playerId": 1001,
    "playerName": "游戏玩家",
    "level": 15,
    "experience": 2500,
    "isOnline": true,
    "lastLogin": "2024-01-15T10:30:00Z",
    "position": {
        "x": 100.5,
        "y": 200.0,
        "z": 50.0
    },
    "skills": ["剑术", "魔法", "治疗"],
    "equipment": {
        "weapon": "传说之剑",
        "armor": "龙鳞甲",
        "accessory": "生命戒指"
    }
}

2. 游戏配置

json 复制代码
{
    "gameSettings": {
        "graphics": {
            "resolution": "1920x1080",
            "quality": "high",
            "fullscreen": false
        },
        "audio": {
            "masterVolume": 0.8,
            "musicVolume": 0.6,
            "sfxVolume": 0.9
        },
        "controls": {
            "mouseSensitivity": 1.0,
            "invertY": false,
            "keyBindings": {
                "moveForward": "W",
                "moveBackward": "S",
                "moveLeft": "A",
                "moveRight": "D"
            }
        }
    }
}

4.3 C#读取存储Json文件

4.3.1 JsonUtility
4.3.1.1 基本概念

什么是JsonUtility:

JsonUtility是Unity内置的JSON序列化工具,提供了线程安全的方法,可以将C#类对象序列化为JSON字符串,也可以将JSON字符串反序列化为C#对象。

核心特点:

  • 内置工具:Unity引擎自带,无需额外导入
  • 线程安全:提供线程安全的序列化方法
  • 简单易用:API简洁,学习成本低
  • 性能优化:针对Unity引擎优化
4.3.1.2 主要方法

核心序列化方法:

csharp 复制代码
// 序列化:将对象转换为JSON字符串
string jsonString = JsonUtility.ToJson(object obj);
string jsonString = JsonUtility.ToJson(object obj, bool prettyPrint);

// 反序列化:将JSON字符串转换为对象
T result = JsonUtility.FromJson<T>(string json);

参数说明:

  • obj:要序列化的对象
  • prettyPrint:是否格式化输出(美化JSON格式)
  • T:目标类型
  • json:JSON字符串
4.3.1.3 使用示例

基于您的脚本的完整示例:

csharp 复制代码
using UnityEngine;
using System.IO;
using System.Collections.Generic;

[System.Serializable]
public class Skill
{
    public int id;
    public string name;
    public int damage;
    
    // 必须有无参构造函数
    public Skill() { }
    
    public Skill(int id, string name, int damage)
    {
        this.id = id;
        this.name = name;
        this.damage = damage;
    }
}

[System.Serializable]
public class Player
{
    public int id;
    public string name;
    public List<int> nums;
    public Skill skill;
    public List<Skill> skills;
    // 注意:Dictionary类型JsonUtility不支持
    // public Dictionary<string,int> dict;
    // public Dictionary<string,Skill> dict2;
}

public class JsonUtilityExample : MonoBehaviour
{
    void Start()
    {
        // 创建测试数据
        Player player = new Player();
        player.id = 1;
        player.name = "张三";
        player.nums = new List<int>(){1, 2, 3};
        player.skill = new Skill(1, "技能1", 100);
        player.skills = new List<Skill>(){
            new Skill(2, "技能2", 200), 
            new Skill(3, "技能3", 300)
        };
        
        // 序列化对象为JSON字符串
        string jsonString = JsonUtility.ToJson(player, true); // 美化输出
        print("序列化结果: " + jsonString);
        
        // 保存到文件
        string filePath = Application.persistentDataPath + "/player.json";
        File.WriteAllText(filePath, jsonString);
        print("文件保存路径: " + filePath);
        
        // 从文件读取JSON字符串
        string loadedJson = File.ReadAllText(filePath);
        
        // 反序列化JSON字符串为对象
        Player player2 = JsonUtility.FromJson<Player>(loadedJson);
        
        // 验证反序列化结果
        if (player2 != null)
        {
            print("反序列化成功!");
            print("玩家ID: " + player2.id);
            print("玩家姓名: " + player2.name);
            print("数字列表第一个: " + player2.nums[0]);
            print("技能名称: " + player2.skill.name);
            print("技能列表第一个: " + player2.skills[0].name);
        }
    }
}
4.3.1.4 优缺点分析

优点:

  • 内置工具:无需额外导入第三方库
  • 性能优秀:针对Unity引擎优化
  • 简单易用:API简洁明了
  • 线程安全:提供线程安全的序列化方法

缺点:

  • 功能限制:不支持Dictionary等复杂类型
  • 特性要求:需要添加[System.Serializable]特性
  • 私有字段:默认不支持私有字段序列化
  • 精度问题:float类型可能有精度误差
4.3.1.5 注意事项

重要限制和注意事项:

1. 序列化特性要求

csharp 复制代码
// 自定义类必须添加[System.Serializable]特性
[System.Serializable]
public class CustomClass
{
    public string name;
    public int value;
}

2. 私有字段序列化

csharp 复制代码
[System.Serializable]
public class CustomClass
{
    public string publicField;        // 可以直接序列化
    
    [SerializeField]                  // 需要添加此特性
    private string privateField;      // 私有字段才能序列化
}

3. 不支持的数据类型

csharp 复制代码
// JsonUtility不支持以下类型:
// - Dictionary<TKey, TValue>
// - 接口类型
// - 委托类型
// - 静态字段
// - 只读字段

4. 构造函数要求

csharp 复制代码
public class Skill
{
    public int id;
    public string name;
    
    // 必须有无参构造函数
    public Skill() { }
    
    public Skill(int id, string name)
    {
        this.id = id;
        this.name = name;
    }
}

5. 精度问题

csharp 复制代码
// float类型在序列化时可能有精度误差
public float preciseValue = 3.14159f;
// 序列化后可能变成3.14159或3.1416
4.3.2 LitJson
4.3.2.1 基本概念

什么是LitJson:

LitJson是一个第三方库,用于处理Json的序列化和反序列化。它是C#编写的,体积小、速度快、易于使用,可以很容易地嵌入到我们的代码中。

核心特点:

  • 第三方库:不属于Unity官方内置工具
  • C#编写:完全由C#语言实现
  • 体积小:代码库占用空间小
  • 速度快:序列化和反序列化性能优秀
  • 易于使用:API设计简洁直观
  • 易于嵌入:只需要将LitJson代码拷贝到工程中即可
4.3.2.2 获取LitJson

获取方式:

  1. 前往LitJson官网
  2. 通过官网前往GitHub获取最新版本代码
  3. 将代码拷贝到Unity工程中即可开始使用LitJson
4.3.2.3 使用LitJson进行序列化

方法:

csharp 复制代码
JsonMapper.ToJson(对象)

注意:

  1. 相对JsonUtility不需要加特性
  2. 不能序列化私有变量
  3. 支持字典类型
  4. 需要引用LitJson命名空间
4.3.2.4 使用LitJson反序列化

方法:

csharp 复制代码
JsonMapper.ToObject<类型>(json字符串)

两种反序列化方式:

1. 动态反序列化(使用JsonData)

csharp 复制代码
// JsonData是LitJson提供的类对象,可以用键值对的形式去访问其中的内容
JsonData data = JsonMapper.ToObject(jsonStr);
print(data["name"]);
print(data["age"]);

2. 强类型反序列化(使用泛型)

csharp 复制代码
// 通过泛型转换更加的方便,建议使用这种方式
MrTang2 t2 = JsonMapper.ToObject<MrTang2>(jsonStr);

注意:

  1. 类结构需要无参构造函数,否则反序列化时报错
  2. 字典虽然支持,但是键在使用为数值时会有问题,需要使用字符串类型
  3. 需要引用LitJson命名空间
4.3.2.5 优缺点分析

优点:

  • 支持Dictionary:原生支持Dictionary等复杂类型
  • 体积小:代码库占用空间小
  • 速度快:序列化和反序列化性能优秀
  • 易于使用:API简洁,学习成本低
  • 易于嵌入:只需拷贝文件即可使用

缺点:

  • 第三方库:不属于Unity官方工具
  • 功能相对简单:相比大型JSON库功能有限
  • 更新维护:依赖第三方维护更新
  • 错误处理:错误信息可能不够详细
4.3.3 JsonUtility和LitJson对比
4.3.3.1 相似点

共同特点:

  • 都是用于JSON序列化和反序列化
  • JSON文档编码格式必须是UTF-8
  • 都是通过静态类调用方法
4.3.3.2 差异点

主要区别:

1. 来源和导入

  • JsonUtility:Unity内置,无需额外导入
  • LitJson:第三方库,需要导入命名空间

2. 特性要求

  • JsonUtility:自定义类使用时需要加特性
  • LitJson:不需要加特性

3. 私有变量支持

  • JsonUtility:支持私有变量(需要加特性)
  • LitJson:不支持私有变量

4. 字典类型支持

  • JsonUtility:不支持字典
  • LitJson:支持字典(但键必须是字符串)

5. 数据集合反序列化

  • JsonUtility:不能直接反序列化数据到数据集合(如数组字典)
  • LitJson:可以

6. 构造函数要求

  • JsonUtility:自定义类不需要有无参构造函数
  • LitJson:需要有无参构造函数

7. null值处理

  • JsonUtility:存储null对象时不会是null,而是默认值的数据
  • LitJson:存储null
4.3.3.3 选择方法

选择标准:

  • 根据项目需求选择合适的工具
  • 考虑性能、功能、维护成本等因素
  • 权衡内置工具和第三方库的优缺点
4.3.3.4 选择建议

推荐使用场景:

选择JsonUtility的情况:

  • 项目对性能要求较高
  • 数据结构相对简单
  • 希望使用Unity官方工具
  • 不需要支持Dictionary类型

选择LitJson的情况:

  • 需要支持Dictionary等复杂类型
  • 对序列化特性没有严格要求
  • 希望更灵活的JSON处理能力
  • 可以接受第三方库的维护成本