ET介绍——强大的MongoBson库
强大的MongoBson库
后端开发,统计了一下大概有这些场景需要用到序列化:
- 对象通过序列化反序列化clone
- 服务端数据库存储数据,二进制
- 分布式服务端,多进程间的消息,二进制
- 后端日志,文本格式
- 服务端的各种配置文件,文本格式
C#序列化库有非常非常多了,protobuf,json等等。但是这些序列化库都无法应当所有场景,既要可读又要小。protobuf不支持复杂的对象结构(无法使用继承),做消息合适,做数据库存储和日志格式并不好用。json做日志格式合适,但是做网络消息和数据存储就太大。我们当然希望一个库能满足上面所有场景,理由如下:
- 你想想某天你的配置文件需要放到数据库中保存,你不需要进行格式转换,后端直接把前端发过来的配置消息保存到数据库中,这是不是能减少非常多错误呢?
- 某天有些服务端的配置文件不用文件格式了,需要放在数据库中,同样,只需要几行代码就可以完成迁移。
- 某天后端服务器crash,你需要扫描日志进行数据恢复,把日志进行反序列化成C#对象,一条条进行处理,再转成对象保存到数据库就完成了。
- 对象保存在数据库,直接就可以看到文本内容,可以做各种类sql的操作
- 想像一个场景,一个配置文本对象,反序列化到内存,通过网络消息发送,存储到数据库中。整个过程一气呵成。
简单来说就是减少各种数据转换,减少代码,提高开发效率,提高可维护性。当然,Mongo Bson就能够满足。MongoDB库既可以序列化成文本也可以序列化成BSON的二进制格式,并且MongoDB本身就是一个游戏中使用非常多的数据库。Mongo Bson非常完善,是我见过功能最全使用最强大的序列化库,有些功能十分贴心。其支持功能如下:
- 支持复杂的继承结构
- 支持忽略某些字段序列化
- 支持字段默认值
- 结构多出多余的字段照样可以反序列化,这对多版本协议非常有用
- 支持ISupportInitialize接口使用,这个在反序列化的时候简直就是神器
- 支持文本json和二进制bson序列化
- MongoDB数据库支持
简单的介绍下mongo bson库
1.支持序列化反序列化成json或者bson
public sealed class Player
{
public long Id; public string Account { get; private set; } public long UnitId { get; set; }
} Player player1 = new Player() { Id = 1 };
string json = player1.ToJson();
Console.WriteLine($"player1 to json: {json}");
Console.WriteLine($"player to bson: {player.ToBson().ToHex()}");
// output:
// player to json: { "_id" : NumberLong(1), "C" : [], "Account" : null, "UnitId" : NumberLong(0) }
// player to bson: B000000125F69640001000000000000000A4163636F756E740012556E6974496400000000000000000000
注意mongo的json跟标准的json有点区别,如果想用标准的json,可以传入一个JsonWriterSettings对象,限制使用JsonOutputMode.Strict模式
// 使用标准json
Player player2 = new Player() { Id = 1 };
Console.WriteLine($"player to json: {player2.ToJson(new JsonWriterSettings() {OutputMode = JsonOutputMode.Strict})}");
// player to json: { "_id" : 1, "C" : [], "Account" : null, "UnitId" : 0 }
反序列化json:
// 反序列化json
Player player11 = BsonSerializer.Deserialize<Player>(json);
Console.WriteLine($"player11 to json: {player11.ToJson()}");
反序列化bson:
// 反序列化bson
using (MemoryStream memoryStream = new MemoryStream(bson))
{
Player player12 = (Player) BsonSerializer.Deserialize(memoryStream, typeof (Player));
Console.WriteLine($"player12 to json: {player12.ToJson()}");
}
2.可以忽略某些字段
[BsonIgnore]该标签用来禁止字段序列化。 public sealed class Player
{
public long Id; [BsonIgnore]
public string Account { get; private set; } public long UnitId { get; set; }
} Player player = new Player() { Id = 2, UnitId = 3, Account = "panda"};
Console.WriteLine($"player to json: {player.ToJson()}");
// player to json: { "_id" : 2, "UnitId" : 3 }
3.支持默认值以及取别名
[BsonElement] 字段加上该标签,即使是private字段也会序列化(默认只序列化public字段),该标签还可以带一个string参数,给字段序列化指定别名。
public sealed class Player
{
public long Id; public string Account { get; private set; } [BsonElement("UId")]
public long UnitId { get; set; }
}
Player player = new Player() { Id = 2, UnitId = 3, Account = "panda"};
Console.WriteLine($"player to json: {player.ToJson()}");
// player to json: { "_id" : 2, "Account" : "panda", "UId" : 3 }
4.升级版本支持
[BsonIgnoreExtraElements] 该标签用在class上面,反序列化时用来忽略多余的字段,一般版本兼容需要考虑,低版本的协议需要能够反 序列化高版本的内容,否则新版本加了字段,旧版本结构反序列化会出错
[BsonIgnoreExtraElements]
public sealed class Player
{
public long Id; public string Account { get; private set; } [BsonElement("UId")]
public long UnitId { get; set; }
}
5.支持复杂的继承结构
mongo bson库强大的地方在于完全支持序列化反序列化继承结构。需要注意的是,继承反序列化需要注册所有的父类,有两种方法: a. 你可以在父类上面使用[BsonKnownTypes]标签声明继承的子类,这样mongo会自动注册,例如:
[BsonKnownTypes(typeof(Entity))]
public class Component
{
}
[BsonKnownTypes(typeof(Player))]
public class Entity: Component
{
}
public sealed class Player: Entity
{
public long Id; public string Account { get; set; } public long UnitId { get; set; }
}
这样有缺陷,因为框架并不知道一个类会有哪些子类,这样做对框架代码有侵入性,我们希望能解除这个耦合 。可以扫描程序集中所有子类父类的类型,将他们注册到mongo驱动中
Type[] types = typeof(Game).Assembly.GetTypes();
foreach (Type type in types)
{
if (!type.IsSubclassOf(typeof(Component)))
{
continue;
} BsonClassMap.LookupClassMap(type);
} BsonSerializer.RegisterSerializer(new EnumSerializer<NumericType>(BsonType.String));
这样完全的自动化注册,使用者也不需要关心类是否注册。
6.ISupportInitialize接口
mongo bson反序列化时支持一个ISupportInitialize接口,ISupportInitialize有两个方法
public interface ISupportInitialize
{
void BeginInit();
void EndInit();
}
BeginInit在反序列化前调用,EndInit在反序列化后调用。这个接口非常有用了,可以在反序列化后执行一些操作。例如
[BsonIgnoreExtraElements]
public class InnerConfig: AConfigComponent
{
[BsonIgnore]
public IPEndPoint IPEndPoint { get; private set; } public string Address { get; set; } public override void EndInit()
{
this.IPEndPoint = NetworkHelper.ToIPEndPoint(this.Address);
}
}
InnerConfig是ET中进程内网地址的配置,由于IPEndPoint不太好配置,我们可以配置成string形式,然后反序列化的时候在EndInit中把string转换成IPEndPoint。 同样我给protobuf反序列化方法也加上了这个调用,参考ProtobufHelper.cs,ET的protobuf因为要支持ilruntime,所以去掉了map的支持,假如我们想要一个map怎么办呢?这里我给生成的代码都做了手脚,把proto消息都改成了partial class,这样我们可以自己扩展这个class,比如:
message UnitInfo
{
int64 UnitId = 1; float X = 2;
float Y = 3;
float Z = 4;
} // protobuf
message G2C_EnterMap // IResponse
{
int32 RpcId = 90;
int32 Error = 91;
string Message = 92;
// 自己的unit id
int64 UnitId = 1;
// 所有的unit
repeated UnitInfo Units = 2;
}
这个网络消息有个repeated UnitInfo字段,在protobuf中其实是个数组,使用起来不是很方便,我希望转成一个Dictionary<Int64, UnitInfo>的字段,我们可以做这样的操作:
public partial class G2C_EnterMap: ISupportInitialize
{
public Dictionary<Int64, UnitInfo> unitsDict = new Dictionary<long, UnitInfo>(); public void BeginInit()
{
} public void EndInit()
{
foreach (var unit in this.Units)
{
this.unitsDict.Add(unit.UnitId, unit);
}
}
}
通过这样一段代码把消息进行扩展一下,反序列化出来之后,自动转成了一个Dictionary。
ET开源地址地址:egametang/ET: Unity3D Client And C# Server Framework (github.com) qq群:474643097
ET介绍——强大的MongoBson库的更多相关文章
- highCharts入门-强大的图表库插件
简介 Highcharts 是一个用纯JavaScript编写的一个图表库, 能够很简单便捷的在web网站或是web应用程序添加有交互性的图表,并且免费提供给个人学习.个人网站和非商业 ...
- cURL 是一个功能强大的PHP库。
使用PHP的cURL库可以简单和有效地去抓网页.你只需要运行一个脚本,然后分析一下你所抓取的网页,然后就可以以程序的方式得到你想要的数据了.无论是你想从从一个链接上取部分数据,或是取一个XML文件并把 ...
- Afreechart很强大的图表库,支持股票曲线图,饼图,曲线
Afreechart是一个很强大的图表库,支持股票曲线图,饼图,曲线等.源码下载:http://www.23code.com/afreechart/
- Android强大的开源库与系统架构工具
后来加上的,因为太强大了,android上百个可立即使用的开源库介绍:https://github.com/Trinea/android-open-project 一款功能强大且实用的开发工具可以为开 ...
- 【全网首发】鸿蒙开源三方组件--强大的弹窗库XPopup组件
目录: 1.介绍 2.效果一览 3.依赖 4.如何使用 5.下载链接 6.<鸿蒙开源三方组件>文章合集 1. 介绍 XPopup是一个弹窗库,可能是Harmony平台最好的弹窗库.它从 ...
- 介绍n款计算机视觉库/人脸识别开源库/软件
计算机视觉库 OpenCV OpenCV是Intel®开源计算机视觉库.它由一系列 C 函数和少量 C++ 类构成,实现了图像处理和计算机视觉方面的很多通用算法. OpenCV 拥有包括 300 多个 ...
- 在别的地方看的<<给程序员介绍一些C++开源库>>,记录给大家共同学习
首先说明这篇文章不是出自我手,大家共同学习. 引用地址:http://oss.org.cn/?action-viewnews-itemid-61998. C++开源库,欢迎补充. C++在“商业应用” ...
- 国内静态文件CDN服务介绍 国内js公共库
国内静态文件CDN服务介绍 新浪SAE 介绍页 文件页 百度云 介绍页 七牛云存储介绍页 优势,可以提交没有的库,支持https,但证书不可信. 又拍云 介绍页 建议使用阿里云OSS自己上传所需文件 ...
- JSP第五篇【JSTL的介绍、core标签库、fn方法库、fmt标签库】
什么是JSTL JSTL全称为 JSP Standard Tag Library 即JSP标准标签库. JSTL作为最基本的标签库,提供了一系列的JSP标签,实现了基本的功能:集合的遍历.数据的输出. ...
- 强大的scrollReveal库,炫酷的页面缓入效果。
首先我问来看一下这个强大的插件能做出什么效果,下面是我找的一个网站: http://kepler.gl/#/, 接下来看看官网给出的效果:https://scrollrevealjs.org/. 是不 ...
随机推荐
- 新零售SaaS架构:什么是订单履约系统?
什么是订单履约系统? 订单履约系统用来管理从接到销售订单,到把货品送到客户手中的整个业务过程.它是上游交易(如销售和客户下单环节)和下游仓储配送(如库存管理.物流)之间的桥梁,确保信息流的顺畅和操作的 ...
- win32-使用EnumWindows比较两个窗口的Z轴
通过使用EnumWindows()和枚举窗口来手动确定EnumChildWindows()来直接确定哪个窗口在z轴上比另一个窗口高. struct myEnumInfo { HWND hwnd1; H ...
- 一秒出图?SDXL-Turbo实时AI绘画整合包下载
SDXL Turbo是一种快速生成的AI构图模型,它基于一种称为对抗性扩散蒸馏的新训练方法,该方法允许在1到4个步骤中以高图像质量对大规模基础图像扩散模型进行采样,并将其与对抗性损失相结合,以确保即使 ...
- Docker实践之08-使用网络
目录 一.外部访问容器 启动容器时指定参数-P(大写P) 启动容器时指定参数-p(小写p) 二.容器互联 使用--link参数使容器互联 将容器加入自定义网络实现互联 三.为容器配置DNS 一.外部访 ...
- 具备有效期的localStorage存储
具备有效期的localStorage存储 类方式 // 具备有效期的localStorage存储-类方式. class LocalStorageWrapper { // 存储数据到localStora ...
- Android Compose开发
目录 好处 入门 Composable 布局 其他组件 列表 verticalScroll 延迟列表 内容内边距 性能 修饰符 偏移量 requiredSize 滚动 添加间距Spacer Butto ...
- java中sha1.md5,base64到底怎么回事
MD5 Message Digest Algorithm MD5(中文名为消息摘要算法第五版)为计算机安全领域广泛使用的一种散列函数,用以提供消息的完整性保护.MD5用的是哈希函数,在计算机网络中应用 ...
- 【Azure 应用服务】当在Azure App Service的门户上 Log Stream 日志无输出,需要如何操作让其输出Application Logs呢?
问题描述 在Azure App Service的门户上 Log Stream 日志无输出,需要如何操作让其输出Application Logs呢? 如下图所示: 问题解答 请注意,上图中提示说:App ...
- Java package(包) +import 关键字(1)
Java 包(package) 为了更好地组织类,Java 提供了包机制,用于区别类名的命名空间. 包的作用 1.把功能相似或相关的类或接口组织在同一个包中,方便类的查找和使用. 2.如同文件夹一样, ...
- PHP四则运算类(支持加、减、乘、除、小中括号)
<?php /** * 四则运算(支持加.减.乘.除.小中括号) * Class calculator */ class calculator { //保留几位小数点 public $point ...