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

相關文章
相關標籤/搜索