Unity数据持久化之XML

目录

  • 数据持久化XML概述
  • XML文件格式
    • XML基本语法
    • XML属性
  • C#读取存储XML
    • XML文件存放位置
    • C#读取XML文件
    • C#存储XML文件
  • 实践小项目
    • 必备知识点
      • XML序列化(不支持字典)
      • XML反序列化
      • IXmlSerializable接口
      • 让Dictionary支持序列化反序列化

数据持久化XML概述

什么是数据持久化
数据持久化就是将内存中的数据模型转换为存储模型,以及
将存储模型转换为内存中的数据模型的统称

说人话:将游戏数据存储到硬盘,硬盘中数据读取到游戏
中,也就是传统意义上的存盘

XML是什么?
全称:可拓展标记语言(EXtensible Markup Language)
XML是国际通用的
它是被设计来用于传输和存储数据的一种文本特殊格式
文件后缀一般为.xml
如何理解不同后缀的文件

文件后缀名决定了文件的格式
不同的软件可以根据后缀名用来判断文件的类型
并且在打开文件时以特定的读取规则去解析它

文件后缀名是人为定的规则
可以有无数种,可以自定义
我们一般使用XML文件来记录和传输数据
XML文档就是使用XML格式配置填写的文档
后缀一般为.xml
我们在游戏中可以把游戏数据按照XML的格式标准
存储在XML文档中,再将XML文档存储在硬盘上或者传输给远端
达到数据持久化的目的

XML文件格式

XML基本语法

推荐xml文本编辑器

只要能打开文档的软件都能打开XML文件
常用的一些打开XML文件的方式
1.系统自带 – 记事本、写字板
2.专用XML文本编辑器 – XMLSpear、STDU XML Editor等等
3.通用文本编辑器 – Sublime Text等等
在这里插入图片描述
创建XML文件

直接右键创建文本,后缀改为xml
选择自己喜欢的用于编辑xml的软件
进行文本编辑
注释

<!---->
<!--在其中写明注释内容-->

固定内容
固定内容代表xml的版本 使用的编码

<?xml version="1.0" encoding="UTF-8"?>

基本语法

<!-- xm1的基本语法 就是<元素标签></元素标签>配对出现 -- >
<PlayerInfo><name>唐老狮</name><age>18</age><sex>true</sex><ItemList><Item><id>1</id><num>10</num></Item><Item><id>2</id><num>10</num></Item></ItemList>
</PlayerInfo>

基本规则

1.每个元素都必须有关闭标签
2.元素命名规则基本遵照C#中变量名命名规则
3.XML标签对大小写敏感
4.XML文档必须有根元素
5.特殊的符号应该用实体引用
&lt —— <小于
&gt —— >大于
&amp——&和号
&apos——'单引号
&quot——“引号

XML属性

<!-- 属性就是在元素标签后面空格 添加的内容 -- >
<!-- 注意:属性必须用引号包裹 单引号双引号都可以 -- >
<Friend name="小明”age='8'>我的朋友</Friend>
<!-- 如果使用属性记录信息 不想使用元素记录 可以如下这样写 -- >
<Fater name ="爸爸”age ="50"/>

如何查语法错误

1.元素标签必须配对
2.属性必须有引号
3.注意命名

或者直接复制到https://www.runoob.com/xml/xml-validator.html
网页上进行验证

注意:一般专门编辑xml的软件都会有判错功能

C#读取存储XML

XML文件存放位置

1.只读不写的XML文件可以放在Resources或者StreamingAssets 文件夹下

2.动态存储的XML文件放在Application.persistentDataPath 路径下

C#读取XML文件

C#读取XML的方法有几种

1.XmlDocument:把数据加载到内存中,方便读取
2.XmlTextReader:以流形式加载,内存占用更少,但是是单向只读,使用不是特别方便,除非有特殊需求,否则不会使用
3.Linq:以后专门讲Ling的时候讲
使用XmlDocument类读取是较方便最容易理解和操作的方法
读取xml文件信息

XmlDocument xml = new XmlDocument();

通过XmlDocument读取xml文件 有两个API
1.直接根据xm1字符串内容 来加载xml文件
存放在Resorces文件夹下的xml文件加载处理

TextAsset asset = Resources.Load<TextAsset>("TestXml");
print(asset.text);
//通过这个方法 就能够翻译字符串为xml对象
xml.LoadXml(asset.trxt));

2.是通过xml文件的路径去进行加载

xml. Load(Application.streamingAssetsPath + "/TestXml.xml");

读取元素和属性信息
节点信息类
XmlNode 单个节点信息类
节点列表信息
XmlNodeList 多个节点信息类
在这里插入图片描述

获取xml中的根节点

xmlNode root = xml.SelectSingleNode("Root");

再通过根节点获取下面的子节点

XmlNode nodeName = root.SelectSingleNode("name");

获取节点包裹的元素信息

print(nodeName.InnerText);

获取属性

//两种方式
XmlNode nodeItem = root.SelectSingleNode("Item");
print(nodeItem.Attributes["id"].Value);
print(nodeItem.Attributes.GetNameItem("id").Value);

获取一个节点下的同名节点

XmlNodeList friendList = root.SelectNodes("Friend");
//迭代器遍历
foreach(XmlNode item in friendList)
{}
//for循环遍历
for(int i = 0;i < friendList.Count;i++)
{print(friendList[i].SelectSingleNode("name").InnerText);
}

总结
1.读取XML文件
XmlDocument xml = new XmlDocument();
读取文本方式1-xml.LoadXml(传入xml文本字符串)
读取文本方式2-xml.Load(传入路径)

2.读取元素和属性
获取单个节点:XmlNode node=xml.SelectSingleNode(节点名)
获取多个节点:XmlNodeList nodeList = xml.SelectNodes(节点名)

获取节点元素内容:node.InnerText
获取节点元素属性:
1.item.Attributes[“属性名”].Value
2.item.Attributes.GetNamedItem(“属性名”).Value

通过迭代器遍历或者循环遍历XmlNodeList对象 可以获取到各单个元素节点

C#存储XML文件

决定存储在哪个文件夹下
注意:存储xml文件 在Unity中一定是使用各平台都可读可写可找到的路径
1,Resources 可读 不可写 打包后找不到 ×
2.Application.streamingAssetsPath 可读 PC端可写 找得到 ×
3.Application.dataPath 打包后找不到 ×
4.Application.persistentDataPath 可读可写找得到 √

string path = Application.persistentDataPath + "/PlayerInfo2.xml";
print(Application.persistentDataPath);

存储xml文件
关键类 XmlDocument 用于创建节点 存储文件
关键类 XmlDeclaration 用于添加版本信息
关键类 XmlElement 节点类

存储有5步
1.创建文本对象

XmlDocument xml = new XmlDocument();

2.添加固定版本信息

XmlDeclaration xmlDec = xml.CreateXmlDeclaration("1.0","UTF-8","");
xml.AppendChild(xmlDec);

3.添加根节点

XmlElement root = xml.CreateElement("Root");
xml.AppendChild(root);

4.为根节点添加子节点

XmlElement name = xml.CreateElement("name");
name.InnerText = "chx";
root.AppendChild(name);
XmlElement itemList = xml.CreateElement("itemList");
itemList.SetAttribute("id",i.ToString());//添加属性
root.AppendChild(itemList);

5.保存

xml.Save(path);

修改xml文件
1.先判断是否存在文件

if(File.Exists(path))
{
}

2.加载后 直接添加节点 移除节点即可

XmlDocument newXml = new XmlDocument();
newXml.Load(path);
XmlNode node = newXml.SelectSingleNode("Root").SelectSingleNode("atk");
//或者
XmlNode node = newXml.SelectSingleNode("Root/atk");
//得到自己的父节点
XmlNode root2 = newXml.SelectSingleNode("Root");
//移除子节点方法
root2. RemoveChild(node);
//添加节点
XmlElement speed = newXml.CreateElement("moveSpeed");
speed.InnerText = "20";
root2.AppendChild(speed);
//保存
newXml.Save(path);

实践小项目

必备知识点

XML序列化(不支持字典)

什么是序列化和反序列化
序列化:把对象转化为可传输的字节序列过程称为序列化
反序列化:把字节序列还原为对象的过程称为反序列化

说人话:
序列化就是把想要存储的内容转换为字节序列用于存储或传递
反序列化就是把存储或收到的字节序列信息解析读取出来使用
xml序列化

public class Lesson1Test
{public int testPublic = 10;private int testPrivate = 11;protected int testProtected = 12;internal int testInternal = 13;public string testPUblicStr = "123";public int testPro { get; set; }public Lesson1Test2 testClass = new Lesson1Test2();
}
public class Lesson1Test2
{public int test1 = 1;public float test2 = 1.1f;public bool test3 = true;
}

1.第一步准备一个数据结构类

Lesson1Test lt = new Lesson1Test();

2.进行序列化
关键知识点
XmlSerializer 用于序列化对象为xml的关键类
Streamwriter 用于存储文件
using 用于方便流对象释放和销毁
第一步:确定存储路径

string path = Application.persistentDataPath + "/Lesson1Test.xml";

第二步:结合 using知识点 和 Streamwriter这个流对象 来写入文件
括号内的代码:写入一个文件流 如果有该文件 直接打开并修改 如果没有该文件 直接新建一个文件
using 的新用法 括号当中包裹的声明的对象 会在 大括号语句块结束后 自动释放掉。当语句块结束,会自动帮助我们调用对象的Dispose方法,让其进行销毁。using一般都是配合内存占用比较大或者有读写操作时进行使用的。

using(StreamWriter stream = new StreamWriter(path))
{//第三步:进行xml文件序列化XmlSerializer s = new XmlSerializer(typeof(Lesson1Test));//这句代码的含义 就是通过序列化对象 对我们类对象进行翻译 将其翻译成		我们的xml文件 写入到对应的文件中//第一个参数:文件流对象//第二个参数:想要备翻译 的对象//注意:翻译机器的类型 一定要和传入的对象是一致的 不然会报错s.Serialize(stream, lt);
}

存储效果:
在这里插入图片描述
自定义节点名或设置属性
可以通过特性设置节点或者设置属性并且修改名字

[XmlAttribute("Test1")]
public int test1 = 1;
[XmlElement("testPublic123123")]
public int testPublic = 10;

XML反序列化

判断文件是否存在

string path = Application.persistentDataPath + "/Lesson1Test.xml";
if(File.Exists(path))
{
}

反序列化
关键知识
1.using 和 StreamReader
2.XmlSerializer的Deserialize反序列化方法

using(StreamReader reader = new StreamReader(path))
{XmlSerializer s = new XmlSerializer(typeof(Lesson1Test));Lesson1Test lt = s.Deserialize(reader) as Lesson1Test;
}

List对象如果有默认值,反序列化时,不会清空,会往后面添加。

IXmlSerializable接口

IXmlSerializable是什么
c#的XmlSerializer 提供了可拓展内容
可以让一些不能被序列化和反序列化的特殊类能被处理
让特殊类继承 IXmlSerializable 接口 实现其中的方法即可
自定义类实践

//继承接口
public class TestLesson3 : IXmlSerializable
{public int test1;public string test2;//返回结构public XmlSchema GetSchema(){return null;}	//反序列化自动调用public void ReadXml(XmlReader reader){//在里面可以自定义反序列化 的规则//1.读属性this.test1 = int.Parse(reader["test1"]);this.test2 = reader["test2"];//2.读节点reader.Read();//这时是读到的test1节点reader.Read();//这时是读到的test1节点包裹的内容this.test1 = int.Parse(reader.Value);//3.读包裹元素节点XmlSerializer s = new XmlSerializer(typeof(int));//跳过根节点reader.Read();reader.ReadStartElement("test1");test1 = (int)s.Deserialize(reader);reader.ReadEndElement();//方式二:while(reader.Read()){if( reader.NodeType == XmlNodeType.Element ){switch (reader.Name){case "test1":reader.Read();this.test1 = int.Parse(reader.Value);break;case "test2":reader.Read();this.test2 = reader.Value;break;}}}}//序列化时自动调用public void WriteXml(XmlWriter writer){//在里面可以自定义序列化 的规则//如果要自定义 序列化的规则 一定会用到 Xmlwriter中的一些方法 来进行序列化//1.写属性writer.WriteAttributeString("test1", this.test1.ToString());writer.WriteAttributeString("test2", this.test2);//2.写节点writer.WriteElementString("test1", this.test1.ToString());writer.WriteElementString("test2", this.test2);//3.写包裹节点XmlSerializer s = new XmlSerializer(typeof(int));writer.WriteStartElement("test1");s.Serialize(writer, test1);writer.WriteEndElement();}
}
//序列化
TestLesson3 t = new TestLesson3();
string path = Application.persistentDataPath + "/TestLesson3.xml";
using(StreamWriter writer = new StreamWriter(path))
{XmlSerializer s = new XmlSerializer(typeof(TestLesson3));s.Serialize(writer,t);
}
//反序列化
using (StreamReader reader = new StreamReader(path))
{//序列化“翻译机器”XmlSerializer s= new XmlSerializer(typeof(TestLesson3));TestLesson3 t2 = s.Deserialize(reader) as TestLesson3;
}

让Dictionary支持序列化反序列化

思考如何让Dictionary支持xml序列和反序列化
1.我们没办法修改C#自带的类
2.那我们可以重写一个类 继承Dictionary 然后让这个类继承序列化拓展接口IXmlSerializable
3.实现里面的序列化和反序列化方法即可
SerizlizerDictionary.cs

using System.Collections;
using System.Collections.Generic;
using System.Xml;
using System.Xml.Schema;
using System.Xml.Serialization;
using UnityEngine;public class SerizlizerDictionary<TKey, TValue> : Dictionary<TKey, TValue>, IXmlSerializable
{public XmlSchema GetSchema(){return null;}//自定义字典的 反序列化 规则public void ReadXml(XmlReader reader){XmlSerializer keySer = new XmlSerializer(typeof(TKey));XmlSerializer valueSer = new XmlSerializer(typeof(TValue));//要跳过根节点reader.Read();//判断 当前不是元素节点 结束 就进行 反序列化while (reader.NodeType != XmlNodeType.EndElement){//反序列化键TKey key = (TKey)keySer.Deserialize(reader);//反序列化值TValue value = (TValue)valueSer.Deserialize(reader);//存储到字典中this.Add(key, value);}reader.Read();}//自定义 字典的 序列化 规则public void WriteXml(XmlWriter writer){XmlSerializer keySer = new XmlSerializer(typeof(TKey));XmlSerializer valueSer = new XmlSerializer(typeof(TValue));foreach (KeyValuePair<TKey, TValue> kv in this){//键值对 的序列化keySer.Serialize(writer, kv.Key);valueSer.Serialize(writer, kv.Value);}}
}

Test.cs

using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Xml.Serialization;
using UnityEngine;public class TestLesson4
{public int test1;public SerizlizerDictionary<int, string> dic;
}public class Lesson4 : MonoBehaviour
{// Start is called before the first frame updatevoid Start(){#region 知识点二 让Dictionary支持序列化和反序列化TestLesson4 tl4 = new TestLesson4();//tl4.dic = new SerizlizerDictionary<int, string>();//tl4.dic.Add(1, "123");//tl4.dic.Add(2, "234");//tl4.dic.Add(3, "345");string path = Application.persistentDataPath + "/TestLesson4.xml";//using(StreamWriter writer = new StreamWriter(path))//{//    XmlSerializer s = new XmlSerializer(typeof(TestLesson4));//    s.Serialize(writer, tl4);//}using (StreamReader reader = new StreamReader(path)){XmlSerializer s = new XmlSerializer(typeof(TestLesson4));tl4 = s.Deserialize(reader) as TestLesson4;}}// Update is called once per framevoid Update(){}
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/323026.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Unity数据持久化之Json

目录 Json概述Json文件格式Json配置规则Excel转Json C#读取存储Json文件JsonUtlityJsonUtlity序列化JsonUtility反序列化 LitJsonLitJson序列化LitJson反序列化JsonUtility和LitJson对比 Json概述 Json是什么? 全称:JavaScript对象简谱(JavaScript Object Notation) Json是国…

修改表空间的状态

Oracle从入门到总裁:​​​​​​https://blog.csdn.net/weixin_67859959/article/details/135209645 表空间有4种状态:联机、脱机、只读和读写。修改其中某一种状态的语句如下所示 设置表空间 tspace 为联机状态。 SQL>ALTER TABLESPACE space ONLINE: 设置表空间 tspa…

【触摸案例-手势解锁案例-按钮高亮 Objective-C语言】

一、我们来说这个self.btns,这个问题啊,为什么不用_btns, 1.我们说,在懒加载里边儿,经常是写下划线啊,_btns,为什么不写,首先啊,这个layoutSubviews:我们第一次,肯定会去执行这个layoutSubviews: 然后呢,去懒加载这个数组, 然后呢,接下来啊,走这一句话, 第一次…

C语言数据在内存中的存储

C语言数据在内存中的存储 个人主页&#xff1a;大白的编程日记 个人专栏&#xff1a;C语言学习之路 文章目录 C语言数据在内存中的存储前言一.整数在内存中的存储1.1整数的表示形式1.2整数在内存中的存放 二.大小端字节序和字节序判断2.1大小端的概念2.2为什么有大小端 三.练习…

利用智能私信软件,快速拓展潜在客户群体

在数字化营销的浪潮中&#xff0c;企业如何快速而有效地触及并吸引潜在客户&#xff0c;已成为一个不可忽视的挑战。随着人工智能技术的不断进步&#xff0c;智能私信软件作为一种新型工具&#xff0c;正逐渐改变着企业的市场拓展方式。本文将探讨如何通过这类软件&#xff0c;…

加州大学欧文分校英语中级语法专项课程04:Intermediate Grammar Project学习笔记(完结)

Intermediate Grammar Project Course Certificate Specialization Certificate Specialization Intro Course Intro 本文是学习 Coursera: Intermediate Grammar Project 这门课的学习笔记。 文章目录 Intermediate Grammar ProjectWeek 01: IntroductionCapstone Introducti…

机器学习——2.损失函数loss

基本概念 损失函数也叫代价函数。损失函数就是计算预测结果和实际结果差距的函数&#xff0c;机器学习的过程就是试图将损失函数的值降到最小。 图左&#xff1a;&#xff5c;t_p - t_c&#xff5c; 图右&#xff1a;&#xff08;t_p - t_c&#xff09;**2 代码实…

docker安装Debian:11 freeswitch1.10.5

文章目录 一、生成一个镜像二、切换一个镜像源为阿里源三、安装一些相关依赖和freeswitch3.1第一步&#xff1a;安装freeswitch-mod和下载所需的依赖项3.2 设置密钥3.3 安装freeswitch所需的依赖项3.4 报错3.4.1 报错13.4.2 报错23.4.3 报错3 四、运行4.1 通话三十秒自动挂断 一…

linux学习:线程池

目录 原理 初始线程池 运行中的线程池 相关结构体 api 线程池初始化 投送任务 增加活跃线程 删除活跃线程 销毁线程池 例子 thread_pool.h thread_pool.c test.c 测试程序 原理 一个进程中的线程就好比是一家公司里的员工&#xff0c;员工的数目应该根据公司的…

【qt】容器的用法

容器目录 一.QVertor1.应用场景2.增加数据3.删除数据4.修改数据5.查询数据6.是否包含7.数据个数8.交换数据9.移动数据10.嵌套使用 二.QList1.应用场景2.QStringList 三.QLinkedList1.应用场景2.特殊点3.用迭代器来变量 四.QStack1.应用场景2.基本用法 五.QQueue1.应用场景2.基本…

LeetCode例题讲解:快乐数

编写一个算法来判断一个数 n 是不是快乐数。 「快乐数」 定义为&#xff1a; 对于一个正整数&#xff0c;每一次将该数替换为它每个位置上的数字的平方和。然后重复这个过程直到这个数变为 1&#xff0c;也可能是 无限循环 但始终变不到 1。如果这个过程 结果为 1&#xff0c…

[uniapp 地图组件] 小坑:translateMarker的回调函数,会调用2次

大概率是因为旋转和移动是两个动画&#xff0c;动画结束后都会分别调用此函数 即使你配置了 【不旋转】它还是会调用两次&#xff0c; 所以此处应该是官方的bug

JavaWeb之Servlet(上)

前言 1. 什么是Servlet (1) Servlet介绍 (2) Servlet运行于支持Java的应用服务器中。 (3) Servlet工作模式&#xff1a; 2. Servlet API 3. 第一个Servlet (1) 创建一个类实现Servlet接口,重写方法。或继承HttpServlet亦可 (2) 在web.xml文档中配置映射关系 标签的执行…

新能源汽车充电站智慧充电电能服务综合解决方案

安科瑞薛瑶瑶18701709087/17343930412 ★解决方案 ✔目的地充电-EMS微电网平台 基于EMS解决方案从设备运维的角度解决本地充电的能量管理及运维问题&#xff0c;与充电管理平台打通数据&#xff0c;为企业微电网提供源、网、荷、储、充一体化解决方案。 ✔运营场站--电能服务…

Burp插件之BurpSuitFake

在最近的测试中发现一个问题 某些高校在在登陆教务平台登陆的时候没有设置验证码 而且重点是 他写的很清楚 密码是身份证后六位 以下是我的一些思路和过程中解决的办法 part1信息搜集 学生的学号是很有规律的 例如我的学号 就是入学年份专业代码班级学号 每个大学学号规则不…

AlphaFold3: Google DeepMind的的新突破

AlphaFold 3的论文今天在Nature期刊发表啦!这可是AI在生物领域最厉害的突破的最新版本。AlphaFold-3的新招就是用扩散模型去"画出"分子的结构。它一开始先从一团模模糊糊的原子云下手,然后慢慢透过去噪把分子变得越来越清楚。 Alphafold3 我们活在一个从Llama和Sora那…

69、oak和华为atlas 200dk A2进行编解码测试

基本思想:将oak深度相机与atlas 200dk A2进行结合,测试其dvpp的编解码能力 cmakelist.txt cmake_minimum_required(VERSION 3.16) project(untitled10) set(CMAKE_CXX_FLAGS "-std=c++11") set(CMAKE_CXX_STANDARD 11) add_definitions(-DENABLE_DVPP_INTERFACE)i…

小红书释放被封手机号 无限注册

前几年抖音也可以释放被封手机号 那时候都不重视 导致现在被封手机号想释放 基本不可能的 或者就是最少几百块 有专业的人帮你通过某些信息差释放 本教程是拆解 小红书被封手机号怎么释放&#xff0c;从今年开始&#xff0c;被封的手机号无法注销了 所以很困扰 那么本教程来…

【ytb数据采集器】按关键词批量爬取视频数据,界面软件更适合文科生!

一、背景介绍 1.1 爬取目标 用Python独立开发的爬虫工具&#xff0c;作用是&#xff1a;通过搜索关键词采集油管的搜索结果&#xff0c;包含14个关键字段&#xff1a;关键词,页码,视频标题,视频id,视频链接,发布时间,视频时长,频道名称,频道id,频道链接,播放数,点赞数,评论数…

nacos下载安装和nacos启动报错

nacos简介: Nacos /nɑ:kəʊs/ 是 Dynamic Naming and Configuration Service的首字母简称&#xff0c;一个更易于构建云原生应用的动态服务发现、配置管理和服务管理平台。 Nacos 致力于帮助您发现、配置和管理微服务。Nacos 提供了一组简单易用的特性集&#xff0c;帮助您…