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 庫。

3:在 Standard 和 Framework 3.5 及更低版本,Swifter.Json 性能可能略減;因為我不敢在這些版本上使用針對性的優化,因為這些版本缺少一些接口,並且可能會在一個未知的平台上運行(如 Unity 和 Xamarin)。

部分 .Net 現有的 JSON 工具特性對比

Feature Comparison

平台兼容性 ✓:兼容大多數平台的大多數版本;乄:兼容部分平台,且版本要求較高;✗:只能在單一平台上運行。

穩定性 ✓:在大多數測試中未出現 BUG;乄:一些不常見操作會出現 BUG;✗:常見操作會出現 BUG。

功能性 ✓:支持大多數的數據類型和方法;乄:支持常用的數據類型和方法;✗:部分常用數據類型和方法不支持。

擴展性 ✓:高度允許自定義格式和處理方式;乄:支持常用的格式設置;✗:不能自定義格式。

高性能 ✓:相比 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.8 更新:

1:將 Swifter.Reflection 整合到 Swifter.Core 中。

2:判斷如果平台支持 Emit 則默認使用 FastObjectRW<T>,如果不支持則默認使用 XObjectRW<T>(因 Xamarin.iOS 不支持 Emit)。

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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM