Swifter.Json -- 在 .Net 平台上的一个功能强大,简单易用,稳定又不失高性能的 JSON 序列化和反序列化工具。

Swifter.Json

Github

Wiki

在 .Net 平台上的一个功能强大,简单易用,稳定又不失高性能的 JSON 序列化和反序列化工具。

Swifter.Json 已经通过了大量测试和线上项目中运行许久来确保它的稳定性。

特性

1: 支持 .Net 上绝大可能是的数据类型,且轻松扩展;包括但不限于:实体,字典,集合,迭代器,数据读取器和表格。

2: 支持 .Net 我已知的大多数平台,包括但不限于:.Net Framework 2.0+, .Net Core 2.0+, .Net Standard 2.0+, Mono, Xamarin, Unity(测试版本为 2018.3).

3: 它几乎是无 BUG 的,若是您遇到了问题,能够在 Github 上发布一个 issue,或者 QQ:1287905882,我会尽力帮助您。

4:全部公开成员都有中文说明,中文语言人的福音😄;未来可能添加英文说明。

缺点

1: 暂没有英文接口说明,但成员命名是英文的。

2:总共有三个 DLL 文件,Swifter.Core(278KB)(这是 Swifter 的核心库,我不但愿它与 Json 挂钩,而是它做为一个巨人,为类库开发者提供不少帮助),Swifter.Unsafe(10KB)(这是用 IL 生成的 DLL,用于指针操做;并非不安全的),Swifter.Json(52KB)(Swifter 的 Json 解析部分);文件不大也不小。

在 .Net Standard 下还须要 System.Reflection.Emit 和 System.Reflection.Emit.Lightweight 库。git

3:在 Standard 和 Framework 3.5 及更低版本,Swifter.Json 性能可能略减;由于我不敢在这些版本上使用针对性的优化,由于这些版本缺乏一些接口,而且可能会在一个未知的平台上运行(如 Unity 和 Xamarin)。

部分 .Net 现有的 JSON 工具特性对比

Feature Comparison

平台兼容性 ✓:兼容大多数平台的大多数版本;乄:兼容部分平台,且版本要求较高;✗:只能在单一平台上运行。github

稳定性 ✓:在大多数测试中未出现 BUG;乄:一些不常见操做会出现 BUG;✗:常见操做会出现 BUG。算法

功能性 ✓:支持大多数的数据类型和方法;乄:支持经常使用的数据类型和方法;✗:部分经常使用数据类型和方法不支持。json

扩展性 ✓:高度容许自定义格式和处理方式;乄:支持经常使用的格式设置;✗:不能自定义格式。数组

高性能 ✓:相比 Newtonsoft 平均快 4x 以上;乄:相比 Newtonsoft 平均快 2x 以上;✗:相比 Newtonsoft 差很少或者更慢。缓存

小分配(内存) ✓:执行过程当中分配的内存极少;乄:必要的内存占用较少;✗:执行过程当中分配的大量的临时内存。安全

大小(文件) ✓:小于 100KB;乄:大于 100KB 小于 500 KB;✗:大于 500 KB。异步

部分 .Net 现有的 JSON 工具性能对比

.Net Core 3.0 Previews running results.

.Net Core 3.0 Previews running results

.Net Framework 4.7.1 Previews running results.

.Net Framework 4.7.1 Previews running results

图中的数字表明用时(ms). 表格颜色随用时从 绿色 渐变为 黄色。当用时超过 3 倍时将以亮黄色显示。工具

Swifter.Json 第一次执行须要额外的时间来生成一个 “操做类(FastObjectRW<T>)” 后续会愈来愈快。因此若是您的程序须要长期运行,那么 Swifter.Json 是您优的选择。若是您的程序不适用这种模式,那么 Swifter.Reflection 的 XObjectRW<T> 也许适合您,详情请看 Wiki。性能

Swifter.Json 的工做原理

如下面的实体类做为例子,解释 Swifter.Json 是如何序列化的。

public class Demo
{
    public int Id { get; set; }

    public string Name { get; set; }
}

当执行如下操做时,

JsonFormatter.SerializeObject(new Demo { Id = 1, Name = "Dogwei" });

Swifter.Json 首先会建立一个 JsonSerializer 实例(此实例是一个 internal class),此类实现了 Swifter.RW.IValueWriter 接口。

而后 Swifter.Json 会执行 Swifter.RW.ValueInterface<Demo>.WriteValue(jsonSerializer, demo); 操做。

在 ValueInterface<Demo>.WriteValue 里会匹配 Demo 的 IValueInterface<Demo> 的实现类;默认状况下,它会匹配到 Swifter.RW.FastObjectInterface<T> 这个实现类。

赋予泛型参数,而后执行 FastObjectInterface<Demo> 的 WriteValue(IValueWriter valueWriter, Demo value) 方法。

在该方法里,它首先检查了 value 是否为 Null,若是是则执行 valueWriter.DirectWrite(null); 方法,表示在 JsonSerializer 写入一个 Null,而后返回。

而后检查 value 的引用,是否为 “父类引用,子类实例” 的对象,若是是则从新匹配子类的 IValueInterface<T> 实现类。

以后是:执行 var fastObjectRW = FastObjectRW<Demo>.Create();,建立一个数据读写器,它实现了 IDataReader<string> 和 IDataWriter<string> 接口。

而后初始化数据读写器:fastObjectRW.Initialize(value);,这至关于把数据读写器中的 Demo 上下文 (Context) 设置为 value。

再调用 valueWriter.WriterObject(IDataReader<string> dataReader); 方法。这就回到了 JsonSerializer 的 WriterObject 方法里。

在该方法里,首先直接写入了一个 '{' 字符。

而后执行 dataReader.OnReadAll(this);,OnReadAll 是用 IL 生成一个方法,它会遍历 Demo 中全部公开属性的名称和值写入到 IDataWriter<string> 里。

这里补充:JsonSerializer 除了实现了 IValueWriter 接口外,还实现了,IDataWriter<string> 和 IDataWriter<int>,这两个是写入 “对象” 和 写入 “数组” 的接口。

在 OnReadAll 里会执行两个操做:dataWriter["Id"].WriteInt32(Context.Id); dataWriter["Name"].WriteString(Context.Name);。

dataWriter["Id"] 操做会写入一个 '"Id":' 的 JSON 字符串;而后返回 IValueWriter 实例,由于 JsonSerializer 自己就是 IValueWriter 的实现类,因此返回它自己。

在 WriteInt32 里,JsonSerializer 器会执行 offset += Swifter.Tools.NumberHelper.Decimal.ToString(value, hGBuffer.GetCharPointer() + offset); Append(',');

补充:hGBuffer 是一个本地内存的缓存,是一个非托管内存,它必需要释放,Swifter.Json 将释放放在 try {} finally {} 里,以确保在任何状况下都会释放。

offset 表示当前 Json 字符串的写入位置。

NumberHelper 是一个高性能低分配的数字算法,主要包括 浮点数和整形的 ToString 算法 和 Parse 算法,它支持 2-64 进制;Decimal 表示十进制。

在 WriteString里 JsonSerializer 器会执行 Append('"'); InternalWriteString(value); Append('"'); Append(',');

在 InternalWriteString 里,JsonSerializer 会根据字符串的长度选择两种写入方式;

第一种方式是扩容字符串两倍的内存空间,而后将字符串所有写入,以确保字符串在包含转义字符时可以完整写入,此方式性能更好。

第二种方式是扩容字符串等量的内存空间,而后逐个字符写入,当内存满的时候再次扩容,直至字符串所有写入。

当字符串长度大于 300 时选用第二种方式,不然选用第一种方式。

这两种方式是参考了其余 JSON 开源库以后最终采用我认为最好的方式,性能对比对应 ShortString 和 LongString 的 ser 测试。

完了以后会返回到 JsonSerializer 的 WriterObject 里,该方法会去掉最后一个 ',' 字符,而后拼上 '}' 字符,而后再拼上 ','。

而后返回到 JsonFormatter 的 SerializeObject 里,该方法会执行 new string(hGBuffer.GetCharPointer(), 0, jsonSerializer.offset - 1); 获取该 JSON 字符串。而后释放 JsonSerializer 器。最后再返回给调用者

释放 JsonSerializer 时,它会一块儿将 hGBuffer 也释放了。

至此,JSON 序列化工做就完成了。

如下解释 Swifter.Json 的反序列化过程。仍是那个 Demo 类。

// 如今咱们获得一个 JSON 字符串。
var json = "{\"Id\":1,\"Name\":\"Dogwei\"}";

执行以下操做:

JsonFormatter.DeserializeObject<Demo>(json);

Swifter.Json 首先会 fixed json 取得 json 的内存地址 pJson;然会执行 var jsonDeserializer = new JsonDeserializer(pJson, 0, json.Length) 建立解析器实例,此类实现了 IValueReader 接口。

而后 Swifter.Json 会执行 ValueInterface<Demo>.ReadValue(jsonDeserializer); 操做。

在 ValueInterface<Demo>.ReadValue 里也是会匹配 Demo 的 IValueInterface<T> 的实现类;它仍是会匹配到 FastObjectInterface<Demo> 这个类。

而后执行 FastObjectInterface<Demo> 的 ReadValue(IValueReader valueReader) 方法。

在该方法里,它进行没有任何判断,直接建立了一个 FastObjectRW<Demo>;由于这里是第二次建立,因此立刻就能建立好。

而后执行 valueReader.ReadObject(IDataWriter<string> dataWriter); 方法。如今回到 JsonDeserializer 的 ReadObject 方法里。

在该方法里,首先判断 JsonValueType 是否等于 Object。若是不是则调用一个 NoObject 方法。

在 NoObject 方法里,若是 JsonValueType 是 String,Number 或 Boolean ,则抛出异常;若是是 Null 则直接返回,若是是 Array,则执行 dataWriter.As<int> 将对象写入器转为 数组写入器,而后调用 ReadArray(IDataWriter<int> dataWriter); 方法。

若是 JsonValueType 是 Object 类型,则执行 dataWriter.Initialize() 操做,此方法内部会执行 Context = new Demo(); 操做。

然会跳过空白字符,找到一个键的开始索引,而后解析这个键获得字符串,如 "Id";若是格式不正确则会引起异常。

Swifter.Json 支持 单引号的键 和 双引号的键 和 没有引号的键。没有引号的键会去除先后空白字符;好比 { Id : 123 } 获得 的键就是 "Id"。

获得键以后,解析器会跳过空白字符,而后判断第一个字符是否等于 ':';若是不是,将会引起 JsonDeserializeException。

此次是 ':' 字符,将索引设为 ':' 处 +1,而后再跳过空白字符,来到 值 的位置,也就是 1 的位置。

此时将调用 dataWriter.OnWriteValue(string name, IValueReader valueReader); 方法来通知对象写入器去读取该值赋给 "Id" 属性。

在 dataWriter.OnWriteValue 内部会根据 name 匹配在 Id 属性,而后执行 Context.Id = valueReader.ReadInt32();

然会回到 JsonDeserializer 的 ReadInt32 方法;该方法会检查当前索引处的 JsonValueType 是否为 Number,若是不是将引起异常或者进行类型转换。

如今 JsonValueType 是 Number,ReadInt32 会首先执行 var numLength = NumberHelper.Decimal.TryParse(pJson + index, length - index, out int result); 操做。

该方法会尝试解析一个常规十进制的整形字符串,并返回解析成功的字符数量,经过判断该返回值是否等于 0 就能得否解析成功。

若是解析成功则 index += numLength; 然会返回。若是不成功则执行 Convert.ToInt32(DirectRead()) 解析;若是仍是解析失败则抛出异常,成功则继续解析。

DirectRead 会解析 Json 的任何值,而后返回一个 object 值。该值多是一个字符串,也多是 double 或 int,也多是字典或集合。这取决于这个 Json 值自己是什么类型。

数字解析完成以后返回到 dataWriter.OnWriteValue 方法,此方法赋值 Id 以后在返回到 dataWriter.ReadObject 方法里。

此时解析器会跳过空白字符,而后获得位于索引处的一个非空白字符;若是此字符为 '}' 则结束解析并返回,若是此字符是 ',' 则尝试解析下一个 键。

注意这里是尝试解析,也就是说若是,此时再次解析到 '}' 则也会正常结束解析;好比:{"Id":123,} 也会被正常解析,但 {"Id":123,,} 则会出现异常。

还有:当解析到 '}' 字符时,若是当前 '}' 对应 JSON 中第一个(根)对象的 '{',那么将结束解析!也就是说若是在该 '}' 以后还有内容的话会被忽略!

若是您想解析相似 {"Id":1}{"Id":2} 相似这样的字符串,须要自定义 IValueInterface<T>。详情请看 Wiki。

特别注意:若是您使用流(TextReader) 的方式进行解析,那么 Swifter.Json 将会读取流的所有内容,可是只解析其第一个(根)对象,以后的内容会被忽略,而且您不能在流中读取到任何内容!

此时找到 ',' 字符,而后跳过空白字符,而后又来到 键 的解析处,解析出 "Name" 仍是同样的找到 ':' 而后跳过空白字符来到 "Dogwei" 的索引处。

再次执行 dataWriter.OnWriteValue("Name", this); 操做来通知对象写入器去读取该值赋给 "Name" 属性。

此时会来到 JsonDeserializer 的 ReadString 方法;该方法一样会检查当前索引处的 JsonValueType 是否为 String,若是不是将引起异常或者进行类型转换。

而后解析器将读取第一个字符('"')做为该字符串的引号,意味着字符串的开始和结束字符。

而后解析器会查找下一个该字符('"'),期间计数 '' 的数量;当出现 '' 字符时,判断下一个字符是否为 'u' 若是是,则跳过包含 '' 在内的 6 个字符("\uAAAA"),若是不是则跳过两个字符("\n")。

遍历完成以后将判断出否出现 '' 转义符,若是没有则直接返回这部分字符串的内容;若是有,则建立一个遍历内容长度减去转义内容的长度的空白字符串,该字符串长度恰好等于结果字符串,而后再次循环填充该字符串。

该方式在没有 '' 转义符时性能极佳,可是若是在有转义符时性能较低,处于中游水平;但内存分配始终时最小的。

如今回到 dataWriter.OnWriteValue 方法里将该值赋予 Context.Name。然会返回解析器。

解析器继续解析会解析到 '}',然会返回到 FastObjectInterface<Demo>.ReadValue,该方法返回 fastObjectRW.Context 给 JsonFormatter.DeserializeObject。

JsonFormatter.DeserializeObject 再返回对象给调用者,解析工做就完成了。

这里还有几个关于 Swifter.Json 的注意事项:

1:Swifter.Json 解析器支持 "\uAAAA" 这样的格式,但序列化时永远也不会将中文字符或其它多字节字符序列化为 "\uAAAA" 格式,我但愿这事由编码器去作。

2:Swifter.Json 解析器支持没有引号或单引号的字符串,可是序列化时绝对不会出现这样的字符串,由于这不是 JSON 标准(Swifter.Json 序列化出来的字符串必定是双引号包围的)。

更新历史

1.2.5 更新:

1:由于更新时疏忽了 Swifter.Core 的引用关系,因此跳过了 1.2.3 和 1.2.4 版本。

2:增长了对相似 1_000_1000 这样的数字值的支持。

2:容许字符串键和值不使用引号包裹!(这样的字符串不能使用先后空格,也不能使用转义符)

4:终于魔鬼打败了天使,Swifter.Json 终于牺牲的部分性能,成了彻底验证的 Json 解析器(除了点 2 和点 3)。

1.2.2 更新:

1:增长了异步方法,JsonFormatter 中以 Async 结尾的方法均为异步方法。

2:修改 Swifter.Extensions.AspNetCore 的扩展使用异步方法。

1.2.1 更新:

1:再度提升性能 (主要原理是对不常见行为禁止内联,提升常见行为的内联成功率)。

2:解决枚举序列化出错,ValueInterface<T>.SetInterface() 不起做用等 BUG。

3:增长特性定义 (反)序列化行为 ([RWFormat], [RWField], [RWObject] 等特性)。

4:增长 AspNetCore 的扩展方法 ConfigureJsonFormatter(this IServiceCollection services)。如今能够很方便将 Swifter.Json 配置到 MVC 了。

5:新增 JsonValue 类,此类能够表示 JSON 反序列化时的任何值(包括对象和数组)。

相关文章
相关标签/搜索