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 反序列化時的任何值(包括對象和數組)。