Swifter.Json
在 .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 工具特性对比

平台兼容性 ✓:兼容大多数平台的大多数版本;乄:兼容部分平台,且版本要求较高;✗:只能在单一平台上运行。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 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 反序列化时的任何值(包括对象和数组)。