65.9K
CodeProject 正在变化。 阅读更多。
Home

fastBinaryJSON

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.79/5 (77投票s)

2012年3月25日

CPOL

12分钟阅读

viewsIcon

400049

downloadIcon

7575

基于fastJSON的二进制JSON序列化器(支持MonoDroid,netcore)

前言

代码现已托管在

引言

fastBinaryJSON 基于我的fastJSON文章(https://codeproject.org.cn/Articles/159450/fastJSON)和代码,它是一个多态对象序列化器。fastBinaryJSON的主要目的是提高数据传输和磁盘存储中数据的序列化和反序列化速度。它是为我即将推出的RaptorDB - 文档数据库引擎而创建的,以追求性能。

特点

fastBinaryJSON具有以下特性列表:

  • 基于fastJSON代码库(非常快,多态)
    • 支持:HashTables、Dictionary、Generic Lists、Datasets,等等。
  • 序列化速度通常提高2-10%,反序列化速度提高17%以上。

为什么?

你可能会问,为什么还需要一个序列化器,为什么不直接使用fastJSON?答案很简单:性能。JSON是一个很棒的格式,但它存在以下问题:

  • JSON是一种文本格式,因此在序列化时会丢失类型信息,这使得再次反序列化数据变得耗时。

为什么不用BSON?

你可能会问,为什么不直接使用BSON(http://bsonspec.org/)?答案如下:

  • 查看上面网站上的规范,你会感到不知所措,因为它很难理解。
  • 你会觉得规范随着时间而演变,许多编码部分已被弃用。
  • BSON将长度编码到流中,这会膨胀数据。对于作者设想的用例来说,这可能没问题,但对于数据传输和存储来说,它只是让东西变得比实际需要更大。
  • 由于长度前缀,数据对象的编码必须分两步完成:一次输出数据,第二次设置长度前缀。

我最初是从在fastJSON上进行BSON转换开始的,但这变得太复杂了,所以被放弃了。

fastBinaryJSON如何编码数据?

JSON是一个极其简单的格式,因此fastBinaryJSON采用了这种简单性,并添加了进行二进制序列化所需的组件。fastBinaryJSON遵循与JSON规范(http://json.org)相同的规则,下表显示了数据的编码方式。

xxxxxx

正如你从上面看到的,所有编码规则都与JSON相同,并且原始数据类型被赋予了1字节的标记来编码数据。因此,通用格式是:

TOKEN,  { DATA } : where DATA can be 0 or more bytes

字符串可以通过两种方式编码:UTF8或Unicode。UTF8更节省空间,而Unicode速度更快。

字符串键或属性名称被编码为特殊的UTF8流,长度限制为255字节,以节省空间(你应该不会有问题,因为大多数属性名称都很短)。

v1.4.19 更改

在此版本中,添加了新的标记。

xxxxx

性能测试

为了了解fastBinaryJSONfastJSON在性能上的差异,执行了以下测试。时间单位为**毫秒**,每个测试对1000个对象进行,重复5次,AVG列是测试的平均值,排除了第一个(受初始化时间影响)。

xxxxxx

正如你在DIFF列([ fastJSON / fastBinaryJSON ])中看到的,序列化器性能至少提高了**2%**,反序列化器性能至少提高了**17%**,其中最大的差异是在DataSet类型上,它包含大量数据行。

Using the Code

要使用fastBinaryJSON,可以使用以下代码示例:

byte[] bytes = fastBinaryJSON.BJSON.ToJSON(obj);
byte[] bytes = fastBinaryJSON.BJSON.ToJSON(obj, true, true); // optimized dataset, 
                                                             // unicode strings

object obj = fastBinaryJSON.BJSON.ToObject(bytes);
SalesInvoice obj = fastBinaryJSON.BJSON.ToObject<SalesInvoice>(bytes); // type is known 

有3个参数控制序列化方式,可以在Instance上设置,也可以像上面的示例一样按调用进行设置。

UseOptimizedDatasetSchema 使用优化的格式序列化Dataset Schema(默认=True
ShowReadOnlyProperties 序列化ReadOnly属性(默认=False
UseUnicodeStrings

string使用Unicode编码(默认=True

附录 v1.3.14.1 - 参数化构造函数

从这个版本开始,fastBinaryJSON现在可以处理没有默认构造函数的参数化构造函数类的反序列化,例如:

public class pctor
{
      public pctor(int a) // pctor() does not exist
      {
      }
}

现在,为了做到这一点,fastBinaryJSON使用了框架中的FormatterServices.GetUninitializedObject(type),它基本上只是为你的类型分配一个内存区域,并将它作为一个对象给你,同时跳过所有初始化,包括构造函数。虽然这非常快,但它有一个不幸的副作用,即忽略所有类初始化,如属性的默认值等。因此,如果你正在恢复对象的局部数据,你应该意识到这一点(如果所有数据都在JSON中并且与类结构匹配,那么你就没问题)。

要控制这一点,可以在BJSONParameters中将ParametricConstructorOverride设置为true

附录 v1.4.0 - 循环引用 & 破坏性更改

从这个版本开始,我修复了一个从一开始就一直困扰我的设计缺陷,即删除了BJSON.Instance单例。这意味着你输入的代码更少,这总是件好事。坏处是,你需要在代码中进行查找和替换。

此外,我发现了一种非常简单快速的方法来支持循环引用的对象结构。因此,像下面的复杂结构将能够正确地序列化和反序列化(单元测试是CircularReferences())。

var o = new o1 { o1int = 1, child = new o3 { o3int = 3 }, o2obj = new o2 { o2int = 2 } };
o.o2obj.parent = o;
o.child.child = o.o2obj; 

为此,fastBinaryJSON会将循环引用替换为:

{"$i" : number } // number is an index for the internal reference

并且在二进制JSON的顶部添加一个$circular : true,以便反序列化器能够识别。

附录 v1.4.19 - 类型化数组

在项目中使用fastBinaryJSON代替BinaryFormatter时,我发现以下代码在客户端失败:

public class DTO
{
  public object Data;
}

// server side
var d = new DTO();
d.Data = new myclass[2]{ new myclass(), new myclass()};
var bytes = BJSON.ToBJSON(d);
TCP_SEND(bytes);

// client side
var bytes = TCP_READ();
DTO dto = BJSON.ToObject(bytes);
// obj.Data will be object[] containing 2 myclass
var obj = (myclass[]) dto.Data; // error here ( unable to convert object[] to myclass[] )
// in v1.4.19 obj.Data will be myclass[]
var obj = (myclass[]) dto.Data; // ok

v1.4.19开始,如果你将T[]放入object数据类型中,fastBinaryJSON将能正确处理它,并在反序列化时得到预期的正确T[],就像使用BinaryFormatter一样。

使用更少的内存

在此版本中,序列化器还将使用更少的内存,只使用一个MemoryStream并将$types放在stream的末尾。这些更改是向后兼容的,不会破坏任何东西。

这些更改希望能帮助你处理大数据传输。

之前的版本

以下是fastBinaryJSON之前版本的列表。

历史

  • 首次发布:2012年3月25日
  • 更新:2012年3月26日
    • 添加了codeplex源代码管理链接
  • 更新 v1.1:2012年5月26日
    • 修复了datetime到本地时间的问题
    • 添加了BJSONParameters
    • 添加了全局类型(与fastJSON的格式相反,以克服名称string的大小限制)
  • 更新 v1.2:2012年7月24日
    • 将反射代码重构到Reflection类中
    • 添加了对顶层struct对象序列化/反序列化的支持
  • 更新 v1.3:2012年8月11日
    • 修复了反射代码中的bug
    • 添加了单元测试
    • ArrayList更改为List<object>
    • 更新了代码,使其与fastJSON类似
    • 反序列化为ToObject< Dictionary<T,V> >
    • 反序列化为ToObject< List<T> >
    • 添加了FillObject方法
  • 更新 v1.3.1:2012年8月16日
    • 修复了$typesarrays中的bug
    • 优化了$types的写入
  • 更新 v1.3.2:2012年9月7日
    • 修复了null对象序列化
    • 为类添加了sealed关键字
    • 修复了SerializeNullValues=false和末尾多余逗号的bug
    • FillObject支持嵌套类型
  • 更新 v1.3.3:2012年9月17日
    • 修复了反序列化零长度数组的bug
    • 测试了德语区域设置的数字
  • 更新 v1.3.4:2012年9月20日
    • 单例使用ThreadStatic来处理并发(感谢Philip Jander
    • 修复了当对象只有一个属性时输出中多余逗号的bug(感谢Philip Jander
  • 更新 v1.3.5:2012年11月16日
    • 添加了对顶层DataSetDataTable反序列化的支持(您必须使用ToObject<DataSet>(...)
    • 添加了dataset测试
    • 添加了MonoDroid项目
  • 更新 v1.3.7:2013年4月20日
    • customtype现在内置了
    • datetime添加了UseUTCTimes属性
    • properttype enum替换为布尔值
    • 使用switch代替链接的if语句
    • 统一了Silverlight和.NET的DynamicMethod
    • SafeDictionary的锁修复
  • 更新 v1.3.8:2013年8月19日
    • 添加了static字段和属性的序列化
    • 修复了禁用输出中的扩展
    • 修复了匿名类型的序列化
    • 添加了对动态对象(dynamic objects)的支持
  • 更新 v1.3.9:2013年8月27日
    • 修复了动态对象和列表
    • 修复了反序列化Dictionary<T, List<V>>Dictionary<T, V[]>
    • 为包含列表的字典添加了测试
  • 更新 v1.3.10:2013年9月11日
    • 修复了哈希表(hashtable)反序列化
    • 为哈希表添加了测试
    • 将getter列表更改为数组,性能提升约3%
    • 删除了未使用的代码
  • 更新 v1.3.11:2013年11月2日
    • 添加了签名程序集
    • 版本号将保持为1.0.0.0以兼容性兼容
    • 文件版本将反映构建号
    • 修复了反序列化为字典而不是dataset(当类型未定义时)的bug
    • 访问动态类型中数组内的属性,例如d.arr[1].a
  • 更新 v1.3.12:2013年11月23日
    • 修复了动态JSON和根数组(如[1,2,3,4])的bug
    • 修复了动态类型中数组内的对象(如[1,2,{"prop":90}])的bug
    • 添加了对特殊集合的支持:StringDictionaryNameValueCollection
  • 更新 v1.3.13:2014年1月10日
    • 修复了与const属性和字段(即被忽略)一起工作的问题
  • 更新 v1.3.14:2014年3月22日
    • 修复了从值和string创建enum的问题
    • dictionary替换了内部部分使用的safedictionary,因此读取时没有锁
    • 添加了自定义忽略属性(感谢Jared Thirsk
    • 使用IsDefined代替GetCustomAttributes(感谢Andrew Rissing
    • 将所有反射代码移出BJSON.cs
    • 现在您可以反序列化非默认构造函数的类(感谢Anton Afanasyev
  • 更新 v1.3.14.1:2014年3月29日
    • ParametricConstructorOverride参数添加了用于控制非默认构造函数的功能
  • 更新 v1.4.0:2014年4月7日
    • *破坏性更改*:删除了BJSON.Instance单例
    • 将BJSON的所有状态移至Reflection单例
    • 所有BJSON接口现在都是static
    • ToObject()添加了BJSONParameters重载
    • 支持循环引用的对象结构
    • 添加了循环测试
  • 更新 v1.4.1:2014年5月2日
    • 修复了obj.List<List<object>>obj.List<object[]>的bug
    • 为方法添加了代码智能提示帮助
    • 添加了ClearReflectionCache()以重置所有内部结构
  • 更新 v1.4.2:2014年8月16日
    • 修复了循环引用的bug(感谢SonicThg
  • 更新 v1.4.3:2014年10月6日
    •  修复了反序列化类中struct属性的bug
  • 更新 v1.4.4:2014年10月23日
    • 修复了反序列化私有设置和无设置属性的bug
    • 为上述情况添加了ReadonlyTest()测试
  • 更新 v1.4.6:2015年1月24日
    • 修复了序列化static字段和属性的bug
    • 跳过对象上的索引器属性(感谢scymen
  • 更新 v1.4.7:2015年2月24日
    • 修复了byte[]作为Dictionary键的bug(感谢Stanislav Lukeš
  • 更新 v1.4.8:2015年3月6日
    • 修复了public static properties的bug
  • 更新 v1.4.9:2015年4月27日
    • 支持多维数组(感谢wmjordan
  • 更新 v1.4.10:2015年5月17日
    • 添加了BJSONParameters.SerializerMaxDepth
    • 性能优化(感谢wmjordan
    • 添加了BJSONSerializer.Dispose()以消除编译器警告
  • 更新 v1.4.11:2015年5月31日
    • 添加了对char类型的支持
    • 增强了dynamic对象处理(感谢Justin Dearing
  • 更新 v1.4.12:2016年3月15日
    • 清理了using语句
    • bug修复:边缘情况CreateArray()时bt为null -> 默认为typeof(object)
  • 更新 v1.4.13:2016年6月19日
    • 修复了ToObject<Dictionary<string, List<X>>>()的bug(感谢sleiN13
    • 修复了CreateStringKeyDictionary()的bug(感谢John Earnshaw
    • 类型访问优化
    • 测试重构
  • 更新 v1.4.14:2016年7月20日
    • 修复了匿名类型中DateTimeInvalidProgramException bug(感谢skottmckay
    • 修复了损坏的自定义类型处理程序(**抱歉大家**)
    • 为自定义类型添加了测试
    • 修复了Dictionary值中的byte[],并添加了测试
  • 更新 v1.4.15:2016年7月23日
    • reflection.csfastJSON同步
    • 修复了只读属性输出的bug
    • 为只读属性添加了测试
    • NonSerialized添加到要忽略的默认属性列表中
    • 支持ExpandoObject序列化,并添加了测试
  • 更新 v1.4.16:2016年9月3日
    • 添加了对接口对象属性的支持(感谢DrDeadCrash
  • 更新 v1.4.17:2016年9月12日
    • 修复了嵌套字典D<,D<,>> 的bug
  • 更新 v1.4.18:2016年10月21日
    • 修复了枚举动态对象的bug
  • 更新 v1.4.19:2016年12月20日
    • 为类型化数组添加了TOKENS.ARRAY_TYPED
    • 正确地将t[]反序列化为object,而不是object[]
    • 添加了BJSONParameters.UseTypedArrays来控制上述功能(默认=true
    • 重命名了主解决方案文件
    • 添加了TOKENS.TYPES_POINTER,用于将$types放在字节流的末尾
    • 优化序列化内存使用
    • 如果将DataSet放入object中,它将正确反序列化
  • 更新 v1.4.20:2017年8月13日
    • 添加了TimeSpan
    • 添加了数字限制测试
  • 更新 v1.4.21:2018年1月19日
    • 修复了反序列化带类型信息的对象数组的bug
    • 为上述情况添加了测试
    • 通过单独的项目支持.NET Core和netstandard2.0
  • 更新 v1.4.23:2018年5月26日
    • 修复了在类中更改BJSONParameters.UsingGlobalTypes时影响原始值的副作用
    • 修复了在没有扩展的情况下使用泛型ToObject<>反序列化嵌套Dictionary的bug
    • 修复了根enum反序列化的bug(感谢al6uiz
    • 支持非公共setter /只读属性(感谢rbeurskens
    • 将reflection.cs与fastJSON统一
  • 更新 v1.5.0:2018年8月8日
    • 将.NET 3.5项目移至单独的文件夹,以避免冲突的obj构建
    • 沿用了fastjson风格的优化
    • 主要的string优化,使用unicodestring时速度提升约46%
    • * 输出字节(使用类型化数组时)存在破坏性更改 *
  • 更新 v1.5.1:2018年8月23日
    • 修复了快速创建没有容量构造函数的列表
    • 修复了.NET35项目中的签名
    • 统一了Reflection.cs
    • 向后兼容v1.4类型化数组数据字节,使用BJSONParameters.v1_4TypedArray
    • * 如果使用RegisterCustomType(),则存在运行时破坏性更改 *
  • 更新 v1.5.2:2019年2月25日
    • 修复了包含sbyte数据的datasets的bug
  • 更新 v1.5.3:2019年6月23日
    • Reflection.csfastJSON v2.2.5同步
  • 更新 v2.3.0 :2019年10月26日
    •  周五13号json攻击检查
© . All rights reserved.