数据持久化Xml与Json

Unity中常用的序列化方式有三种,二进制序列化,xml,json
二进制序列化因为不能维护字段,通常用于图片,音频,视频等文件的传输
Xml相比Json有阅读直白的优势,通常用于配置表
Json效率比Xml高,常用于网络传输

Unity还提供了PlayerPrefs类进行数据持久化,只适用于一些小游戏.

Xml序列化与反序列化

代码

using System;
using System.IO;
using System.Xml.Serialization;
using UnityEngine;

public class SerializeTool {
    /// <summary>
    /// xml序列化
    /// </summary>
    /// <param name="path"></param>
    /// <param name="obj"></param>
    public static void XmlSerialize(string path, object obj) {
        try
        {
            if (File.Exists(path))
            {
                File.Delete(path);//已经存在同名文件就删除
            }
            //创建新的文件 using语法可以在范围结束后自动调用Dispose方法
            using (FileStream fs = new FileStream(path, FileMode.Create, FileAccess.ReadWrite, FileShare.ReadWrite))
            {
                //设置utf8编码 防止乱码
                using (StreamWriter sw = new StreamWriter(fs, System.Text.Encoding.UTF8))
                {
                    XmlSerializer xs = new XmlSerializer(obj.GetType());
                    xs.Serialize(sw, obj);//序列化
                }
            }
        }
        catch (Exception e)
        {
            Debug.Log(path + ":" + e);
        }
    }
    /// <summary>
    /// xml反序列化
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="path"></param>
    /// <returns></returns>
    public static T XmlDeserialize<T>(string path) where T :class {
        T t = default(T);
        try
        {
            //using语法可以在范围结束后自动调用Dispose方法
            using (FileStream fs = new FileStream(path,FileMode.Open,FileAccess.ReadWrite,FileShare.ReadWrite))
            {
                //反序列化
                XmlSerializer xs = new XmlSerializer(typeof(T));
                t = (T)xs.Deserialize(fs);
                //fs.Dispose();
            }
        }
        catch (Exception e)
        {
            Debug.Log(path + ":" + e);
        }
        return t;
    }
}

调用一下代码生成xml文件测试,觉得exsl给策划,然后转回xml作为物品道具之类的属性表,非常合适.

<?xml version="1.0" encoding="ISO-8859-1"?>
<StarData xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<position>
<x>104.39492</x>
<y>110.7</y>
<z>101.098129</z>
</position>
<rotation>
<x>0</x>
<y>0</y>
<z>0</z>
<w>1</w>
<eulerAngles>
<x>-0</x>
<y>0</y>
<z>0</z>
</eulerAngles>
</rotation>
<entityName>Star(Clone)147</entityName>
<speedX>22.6322365</speedX>
<speedY>-5.658059</speedY>
<speedZ>-56.58059</speedZ>
<lowestHeight>35</lowestHeight>
<duration>2</duration>
</StarData>

xml标签

此外xml有一些标签可供选择,会对生成的xml格式有一些影响
[XmlElement(“testString”)]//设置属性名字
[XmlAttribute(“testString”)]//设置该属性为特性
[XmlType(“testString”)]//打在类名前,设置类的名字
[XmlArray(elementName: “testString”)]//设置列表名
[XmlArrayItem(elementName: “testString”)]//设置列表中的元素名
[XmlIgnore]//忽略该字段

Json

目前比较流行的,效率高的json框架是litjson.
litjson如果想使用自动序列化/解析时,有几个需要注意的地方
1.它不支持float类型数据
2.字典第一个数据类型必须为string
3.类与js串上的字段必须完全一致,导致了无法更新字段

不过我们可以为每一个类编写序列化/反序列化方法,缺点是工作量有点大.
介于以上种种原因,我们使用litjson为每一个类编写序列化和反序列化方法

代码

我们将json序列化和反序列化的方法封装成下面的格式.
注意不要这样打log,因为sr.ReadToEnd()中的值只能读取一次,下一次sb中就空值了

Debug.Log(sr.ReadToEnd());
StringBuilder sb = new StringBuilder(sr.ReadToEnd());//读取全部数据
/// <summary>
/// json序列化到本地
/// </summary>
/// <param name="path"></param>
/// <param name="json"></param>
public static void JsonSerialize(string path, string json)
{
    try
    {
        if (File.Exists(path))
        {
            File.Delete(path);//已经存在同名文件就删除
        }
        //创建新的文件 using语法可以在范围结束后自动调用Dispose方法
        using (FileStream fs = new FileStream(path, FileMode.Create, FileAccess.ReadWrite, FileShare.ReadWrite))
        {
            //设置utf8编码 防止乱码
            using (StreamWriter sw = new StreamWriter(fs, Encoding.UTF8))
            {
                sw.WriteLine(json);
            }
        }
    }
    catch (Exception e)
    {
        Debug.Log(path + ":" + e);
    }
}
/// <summary>
/// 读取json文件
/// </summary>
/// <param name="path"></param>
/// <returns></returns>
public static JsonData JsonDeserialize(string path)
{
    try
    {
        //using语法可以在范围结束后自动调用Dispose方法
        using (FileStream fs = new FileStream(path, FileMode.Open, FileAccess.ReadWrite, FileShare.ReadWrite))
        {
            using (StreamReader sr = new StreamReader(fs, Encoding.UTF8))
            {
                StringBuilder sb = new StringBuilder(sr.ReadToEnd());//读取全部数据
                JsonData jsonData = JsonMapper.ToObject(sb.ToString());//获取jsonData
                return jsonData;
            }
        }
    }
    catch (Exception e)
    {
        Debug.Log(path + ":" + e);
    }
    return null;
}

我们在基类中定义两个方法,一个用于获取完整的json序列化,一个用于续写序列化方法
再定义一个反序列化的方法

public abstract class BaseData {
    public Vector3 position;//物体的位置
    public Quaternion rotation;//物体的旋转
    public string entityName;//实体的名字
    /// <summary>
    /// 获取序列化js串
    /// </summary>
    /// <returns></returns>
    public string GetJson() {
        StringBuilder sb = new StringBuilder();
        JsonWriter jw = new JsonWriter(sb);
        jw.WriteObjectStart();//开始写入
        ToJson(jw);
        jw.WriteObjectEnd();//结束写入
        return sb.ToString();
    }
    /// <summary>
    /// 序列化方法 可续写
    /// </summary>
    /// <param name="jw"></param>
    public virtual void ToJson(JsonWriter jw) {
        if (jw==null)
        {
            throw new Exception("null");
        }
        jw.WritePropertyName("position");//写入第一个属性
        jw.WriteObjectStart();//开始写入集合
        jw.WritePropertyName("x");
        jw.Write(position.x);
        jw.WritePropertyName("y");
        jw.Write(position.y);
        jw.WritePropertyName("z");
        jw.Write(position.z);
        jw.WriteObjectEnd();//结束写入集合
        jw.WritePropertyName("rotation");
        jw.WriteObjectStart();//开始写入集合
        jw.WritePropertyName("w");
        jw.Write(rotation.w);
        jw.WritePropertyName("x");
        jw.Write(rotation.x);
        jw.WritePropertyName("y");
        jw.Write(rotation.y);
        jw.WritePropertyName("z");
        jw.Write(rotation.z);
        jw.WriteObjectEnd();//结束写入集合
        jw.WritePropertyName("entityName");
        jw.Write(entityName);
    }
    /// <summary>
    /// 写入json数据
    /// </summary>
    public virtual void SetJsonData(JsonData data)
    {
        if (data == null)
        {
            throw new Exception("null");
        }
        JsonData positionJson = data["position"];
        position.x = float.Parse(positionJson["x"].ToString());//物体的位置
        position.y = float.Parse(positionJson["y"].ToString());//物体的位置
        position.z = float.Parse(positionJson["z"].ToString());//物体的位置
        JsonData rotationJson = data["rotation"];
        rotation.w = float.Parse(rotationJson["w"].ToString());
        rotation.x = float.Parse(rotationJson["x"].ToString());
        rotation.y = float.Parse(rotationJson["y"].ToString());
        rotation.z = float.Parse(rotationJson["z"].ToString());
        entityName = data["entityName"].ToString();//实体的名字
    }
}

在子类中续写一下序列化和反序列化的方法,规避一下重复敲代码

using LitJson;

public class StarData : BaseData {
    public float speedX;//X轴速度
    public float speedY;//Y轴速度
    public float speedZ;//Z轴速度
    public float lowestHeight;//低于lowestHeight高度后开始拖尾特效
    public float duration;//持续时间
    /// <summary>
    /// 续写序列化方法
    /// </summary>
    /// <param name="jw"></param>
    public override void ToJson(JsonWriter jw)
    {
        base.ToJson(jw);
        jw.WritePropertyName("speedX");
        jw.Write(speedX);
        jw.WritePropertyName("speedY");
        jw.Write(speedY);
        jw.WritePropertyName("speedZ");
        jw.Write(speedZ);
        jw.WritePropertyName("lowestHeight");
        jw.Write(lowestHeight);
        jw.WritePropertyName("duration");
        jw.Write(duration);
    }
    /// <summary>
    /// 写入json数据
    /// </summary>
    public override void SetJsonData(JsonData data)
    {
        base.SetJsonData(data);
        speedX = float.Parse(data["speedX"].ToString());
        speedY = float.Parse(data["speedY"].ToString());
        speedZ = float.Parse(data["speedZ"].ToString());
        lowestHeight = float.Parse(data["lowestHeight"].ToString());
        duration = float.Parse(data["duration"].ToString());
    }
}

接下来我们随便构造一个数据类 ,然后调用

SerializeTool.JsonSerialize(Application.streamingAssetsPath + "/star.json", star.data.GetJson());

将序列化的json保存到本地
检查一下数据格式

{"position":{"x":135.904724121094,"y":110.699996948242,"z":49.5925445556641},"rotation":{"w":1.0,"x":0.0,"y":0.0,"z":0.0},"entityName":"Star(Clone)5","speedX":-57.2221565246582,"speedY":-5.72221565246582,"speedZ":22.8888626098633,"lowestHeight":35.0,"duration":2.0}

然后定义一个StarData类型的数据,读取本地刚刚保存的js数据文件,并将json解析保存进这个类中.

data.SetJsonData(SerializeTool.JsonDeserialize(Application.streamingAssetsPath + "/star.json"));

最后从基类和子类选几个属性,简单的测试一下,没有问题
在这里插入图片描述
我想,还差个加密,就可以用于单机游戏存档了吧.
这个方法写起来还是比较麻烦的,要手动给所有数据类编写序列化,反序列化方法.但是规避了上面的一些缺点.
如果有更好的实现方法,欢迎指教.

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值