javascript
开源 , KoobooJson一款高性能且轻量的JSON框架
在我正式介紹KoobooJson之前,我要介紹一下什么是Kooboo!
Kooboo是我們老板用C#編寫的一個開源的非常神奇的網(wǎng)站開發(fā)工具,它是一個類CMS生成器,但其從數(shù)據(jù)庫,前端引擎,到各種網(wǎng)絡(luò)協(xié)議服務(wù)器都是用c#自主創(chuàng)造的,幾乎很少使用到第三方庫,它編譯后的發(fā)布版本僅有幾M,而正是因為僅僅只有幾M,為了Json框架不要太影響主程序的大小,這才有了KoobooJson此次的誕生!
在NuGet包中可以直接搜索 KoobooJson 下載使用即可
什么是KoobooJson?
KoobooJson的優(yōu)點
小巧
快速
覆蓋類型廣
KoobooJson的實現(xiàn)? (后續(xù)我將出一篇新的文章詳細(xì)講解實現(xiàn))
序列化
反序列化
功能介紹
忽略注釋
忽略互引用所導(dǎo)致的堆棧循環(huán)
忽略Null值
排序特性
忽略序列化元素
序列化時僅包含該元素
時間格式
首字母大小寫
別名特性
反序列化時指定構(gòu)造函數(shù)
值格式化特性
全局Key格式化
其它
KoobooJson的優(yōu)點
1. 小巧
目前KoobooJson只有130k, 并且沒有任何額外依賴項, KoobooJson當(dāng)前支持框架版本.NET4.5 .NET Core2+ .NET Standard 2
2. 快速
KoobooJson 遵循JSON?RFC8259規(guī)范, 是一款適用于C#的快速的Json文本序列化器
它基于表達(dá)式樹構(gòu)建, 在運(yùn)行時會動態(tài)的為每個類型生成高效的解析代碼, 這過程包括: 利用靜態(tài)泛型模板進(jìn)行緩存, 避免字典查詢開銷, 避免裝箱拆箱消耗, 緩沖池復(fù)用, 加速字節(jié)復(fù)制...
KoobooJson生成代碼的方式并沒有采用Emit, 而是采用ExpressionTree. ExpressionTree相比Emit而言, 它不能像Emit直接寫出最優(yōu)的IL代碼, 它依賴于下層的編譯器, 在某些時候它會多生成一些不必要的IL代碼路徑, 故而性能上有所遜色. 但相較于幾乎沒有類型檢查的Emit而言, ExpressionTree不會出現(xiàn)各種莫名其妙的錯誤, 它更加安全, 也更加容易擴(kuò)展維護(hù).
雖然ExpressionTree與Emit相比在性能方面可能會有所差異, 但是KoobooJson的表現(xiàn)卻相當(dāng)亮眼!
?
上圖是使用BenchmarkDotNet在Net Core2.1上做的Json序列化和反序列化的性能測試,隨機(jī)生成大量的測試數(shù)據(jù),迭代100次后產(chǎn)生的結(jié)果,基準(zhǔn)報告在這里
BenchmarkDotNet=v0.11.4, OS=Windows 10.0.17763.316 (1809/October2018Update/Redstone5)?Intel Core i7-8550U CPU 1.80GHz (Kaby Lake R), 1 CPU, 8 logical and 4 physical cores?.NET Core SDK=2.1.505?[Host] : .NET Core 2.1.9 (CoreCLR 4.6.27414.06, CoreFX 4.6.27415.01), 64bit RyuJIT?Job-XEQPPS : .NET Core 2.1.9 (CoreCLR 4.6.27414.06, CoreFX 4.6.27415.01), 64bit RyuJIT
IterationCount=100 LaunchCount=1 WarmupCount=1
3. 覆蓋類型廣
在類型定義上, KoobooJson并沒有單獨實現(xiàn)每個集合或鍵值對類型, 而是對這些FCL類型進(jìn)行劃分成不同的模板
a. KoobooJson將序列化分為5種類型:
原始類型?
它包括 Boolean, Byte, SByte, Int16, UInt16, Int32, UInt32, Int64, UInt64, IntPtr, UIntPtr, Char, Double, and Single.所有擁有鍵值對行為的類型
任何能夠?qū)崿F(xiàn)IDictionary<>或能夠?qū)崿F(xiàn)IDictionary且能夠通過構(gòu)造函數(shù)注入鍵值對的類型, 都將以鍵值對方式進(jìn)行解析所有擁有集合行為的類型?
任何能夠?qū)崿F(xiàn)IEnumable并且滿足IColloction的Add行為或擁有自己獨特的集合行為且能夠通過構(gòu)造函數(shù)注入集合的類型, 都將以集合方式進(jìn)行解析特殊類型?
如Nullable<>, Lazy<>, Guid, Datatable, DateTime, Type, Task, Thread, Timespan...等等這些特定的類型實現(xiàn)常規(guī)Model的鍵值對類型?
在KoobooJson中, 如果當(dāng)類型不滿足上述4種時, 將會以鍵值對的形式來對其解析, KoobooJson會對Model中公開的所有元素進(jìn)行序列化, 在這個環(huán)節(jié), 幾乎配置器中所有的配置都是有關(guān)Model的. 諸如別名, 忽略特性, 指定構(gòu)造函數(shù), 忽略堆棧循環(huán)引用, 首字母大小寫, 格式化器... 值得一提的是, 在對接口類型進(jìn)行反序列化時, KoobooJson默認(rèn)會自動創(chuàng)建并返回一個實現(xiàn)于該接口的對象.
b. 在對類型的解析上, 其中浮點型,日期時間類型, GUID的解析是參照了JIL的代碼, 在此表示感謝.
作為一款活躍的Json庫, KoobooJson會不斷支持更多的類型, 這其中, 因為對FCL中的鍵值對和集合的行為進(jìn)行歸納, 所以對于這兩種類型, KoobooJson并不像其它框架一樣去特定的為每種類型單獨實現(xiàn), 實際上, 第2和3所定義的規(guī)則可以容納FCL中的大多數(shù)鍵值對或集合類型.
目前KoobooJson所覆蓋的類型包括?:?Hashtable, SortedList, ArrayList, IDictionary, Dictionary<,>, IList,List<>, IEnumerable<>, IEnumerable, ICollection, ICollection<>, Stack<>, Queue<>, ConcurrentBag<>, ConcurrentQueue<>, ConcurrentStack<>, SortedDictionary<,>, ConcurrentDictionary<,>, SortedList<,>, IReadOnlyDictionary<,>, ReadOnlyDictionary<,>, ObservableCollection<>, HashSet<>, SortedSet<>, LinkedList<>, ReadOnlyCollection<>, ArraySegment<>, Stack, Queue, IReadOnlyList<>, IReadOnlyCollection<>, ReadOnlyCollection<>, ISet<>, BitArray, URI, NameValueCollection, StringDictionary, ExpandoObject, StringBuilder, Nullable<>, Lazy<>, Guid, Datatable, DateTime, Type, Task, Thread, Timespan, Enum, Exception, Array[], Array[,,,,,]...
KoobooJson的實現(xiàn)
序列化
class UserModel{
public object Obj;
public string Name;
public int Age;
}
string json = JsonSerializer.ToJson(new UserModel());
在對類型第一次序列化時, KoobooJson會為這個類型生成大致是這樣的解析代碼.
void WriteUserModel(UserModel model,JsonSerializerHandler handler){
...配置選項處理...格式化器處理...堆棧無限引用處理...
handler.sb.Write("Obj:")
WriteObject(model.Obj);//在序列化時將為Object類型做二次真實類型查找
handler.sb.Write("Name:")
WriteString(model.Name);
handler.sb.Write("Age:")
WriteInt(model.Age);
}
如果是List<UserModel>的話, 那么將生成這樣的代碼
handler.sb.Write("[")foreach(var user in users)
{
WriteUserModel(user);
WriteComma()
}
handler.sb.Write("]")
在當(dāng)前版本中, KoobooJson序列化使用的容器為StringBuilder, 與直接ref char[]相比, 多了一些額外的調(diào)用.?將考慮在下個版本中構(gòu)建一個輕便的char容器, 并會區(qū)分對象大小, 考慮棧數(shù)組和通過預(yù)掃描大小來減少對內(nèi)存的開銷,這將顯著提升序列化速度.
反序列化
在對類型進(jìn)行第一次反序列化時, KoobooJson會為這個類型生成大致是這樣的解析代碼.
UserModel model = JsonSerializer.ToObject<UserModel>("{\"Obj\":3,\"Name\":\"Tom\",\"Age\":18}");void ReadUserModel(string json,JsonDeserializeHandler handler)
{
...Null處理...
ReadObjLeft()
空元素處理...構(gòu)造函數(shù)處理...配置項處理...格式化器處理...
while(i-->0){
switch(gechar())
{
case 'O':
switch(getchar())
case 'b':
switch(getchar())
case 'j':
ReadQuote();
ReadObject();
if(getchar()==',')
i++;
}
}
ReadObjRight()
}
KoobooJson生成反序列化代碼, KoobooJson會假設(shè)json格式完全正確, 沒有預(yù)先讀取Json結(jié)構(gòu)部分, 而是直接使用代碼來描述結(jié)構(gòu), 所以KoobooJson少了一次對json結(jié)構(gòu)的掃描, 執(zhí)行過程中如果json結(jié)構(gòu)發(fā)生錯誤, 會直接拋出異常.
而對于key的匹配, KoobooJson生成的是逐個char的自動機(jī)匹配代碼, 目前KoobooJson是以字典樹為算法, 逐個char進(jìn)行類型比較, 與一次比較多個char相比, 這種方式顯然沒有達(dá)到最小的查詢路徑, 不過在jit優(yōu)化下, 兩種方式實現(xiàn)經(jīng)測試效率幾乎一樣.
在反序列化讀取字符時, 因為是對類型動態(tài)生成編碼, 提前知道每個類型中的元素的字節(jié)長度和其類型的值長度, 所以KoobooJson出于更高的性能對反序列化采取了指針操作, 并加速字節(jié)讀取.
case 3:if (*(int*)Pointer != *(int*)o) return false;
if (*(Pointer + 2) != *(o + 2)) return false;
goto True;
case 4:
if (*(long*)Pointer != *(long*)o) return false;
goto True;
case 5:
if (*(long*)Pointer != *(long*)o) return false;
if (*(Pointer + 4) != *(o + 4)) return false;
因為是指針操作, KoobooJson在反序列化環(huán)節(jié)幾乎不需要去維護(hù)一個char池來存放下一個需要讀取的json結(jié)構(gòu)片段.
功能介紹
KoobooJson當(dāng)前僅支持3個API調(diào)用
string Kooboo.Json.JsonSerializer.ToJson<T>(T value, JsonSerializerOption option=null)T Kooboo.Json.JsonSerializer.ToObject<T>(string json, JsonDeserializeOption option=null)
object Kooboo.Json.JsonSerializer.ToObject(string json, Type type, JsonDeserializeOption option=null)
忽略注釋
在json字符串的讀取中KoobooJson會自動忽略注釋
string json = @"/*注釋*/
{//注釋
/*注釋*/""Name"" /*注釋*/: /*注釋*/""CMS"" /*注釋*/,//注釋
/*注釋*/
""Children"":[//注釋
1/*注釋*/,
2/*注釋*/
]//注釋
}//注釋
/*此處*/
";
var obj = JsonSerializer.ToObject(json);
obj=>Name:CMS
obj=>Children:Array(2)
忽略互引用所導(dǎo)致的堆棧循環(huán)
class A{
public B b;
}
class B
{
public A a;
}
A.b=B;
B.a=A;
A指向B, B指向A, 在序列化時這種情況會發(fā)生無限循環(huán).可通過KoobooJson的序列化配置項中的屬性來設(shè)定這種情況下所對應(yīng)的結(jié)果
JsonSerializerOption option = new JsonSerializerOption{
ReferenceLoopHandling = JsonReferenceHandlingEnum.Null
};
string json = JsonSerializer.ToJson(a, option);
json => {\"b\":{\"a\":null}}
------
ReferenceLoopHandling = JsonReferenceHandlingEnum.Empty
json => {\"b\":{\"a\":{}}}
-----
ReferenceLoopHandling = JsonReferenceHandlingEnum.Remove
json => {\"b\":{}}
忽略Null值
class A{
public string a;
}
A.a=null;
JsonSerializerOption option = new JsonSerializerOption { IsIgnoreValueNull = true };
var json = JsonSerializer.ToJson(A, option);
json => {}
排序特性
class A{
[JsonOrder(3)]
public int a;
[JsonOrder(2)]
public int b;
[JsonOrder(1)]
public int c;
}
可通過[JsonOrder(int orderNum)]來排序序列化的json元素位置.?如果是正常沒有通過[JsonOrder]排序元素,那么解析出來的Json則是默認(rèn)順序:{"a":0,"b":0,"c":0}?上面樣例通過[JsonOrder]排序后是這樣的:{"c":0,"b":0,"a":0}
忽略序列化元素
class A{
[IgnoreKey]
public int a;
public int b;
}
可通過[IgnoreKey]特性來標(biāo)記序列化和反序列化要忽略的元素 json => {"b":0}?當(dāng)然, 也可以通過配置來動態(tài)選擇忽略對象
JsonSerializerOption option = new JsonSerializerOption { IgnoreKeys = new List<string>(){"b"} };var json = JsonSerializer.ToJson(A, option);
json => {}
序列化時僅包含該元素
class A{
[JsonOnlyInclude]
public int a;
public int b;
public int c;
}
json => {\"a\":0}
如果一個model里包含幾十個元素, 而你僅想序列化其中一個, 那么就沒必要對每一個元素進(jìn)行[IgnoreKey]標(biāo)記,只需要對想要序列化的元素標(biāo)記[JsonOnlyInclude]即可
時間格式
JsonSerializerOption option = new JsonSerializerOption { DatetimeFormat=DatetimeFormatEnum.ISO8601 };json => 2012-01-02T03:04:05Z
JsonSerializerOption option = new JsonSerializerOption { DatetimeFormat=DatetimeFormatEnum.RFC1123 };
json => Thu, 10 Apr 2008 13:30:00 GMT
JsonSerializerOption option = new JsonSerializerOption { DatetimeFormat=DatetimeFormatEnum.Microsoft };
json => \/Date(628318530718)\/
首字母大小寫
class A{
public int name;
}
JsonSerializerOption option = new JsonSerializerOption { JsonCharacterRead=JsonCharacterReadStateEnum.InitialUpper };
json => {\"Name\":0}
在對model序列化時可以指定key的首字母大小寫,反序列化時也可以設(shè)置對字符串不區(qū)分大小寫.首字母大小寫屬于內(nèi)嵌支持, 在解析時并不會影響性能
別名特性
class A{
[Alias("R01_Name")]
public int name;
}
json => {\"R01_Name\":0}
當(dāng)元素被標(biāo)記[Alias]后,KoobooJson無論序列化還是反序列化都會按照Alias來進(jìn)行解析
反序列化時指定構(gòu)造函數(shù)
class A{
public A(){}
[JsonDeserializeCtor(3,"ss")]
public A(int a,string b){}
}
在反序列化的時候, 我們不得不調(diào)用構(gòu)造函數(shù)來以此創(chuàng)建對象.?在常規(guī)情況下, KoobooJson會通過優(yōu)先級自動搜索最合適的構(gòu)造函數(shù),其優(yōu)先級順序為:?public noArgs => private noArgs => public Args => private Args, 這其中, 會對有參構(gòu)造函數(shù)進(jìn)行默認(rèn)值構(gòu)造.
然而你也可以顯式通過[JsonDeserializeCtor(params object[] args)]特性來指定反序列化時的構(gòu)造函數(shù),?這樣 當(dāng)KoobooJson創(chuàng)建A實例的時候就不是通過new A(); 而是new A(3,"ss");
值格式化特性
class A{
[Base64ValueFormat]
public byte[] a;
}
當(dāng)你需要來覆寫由KoobooJson進(jìn)行元素解析的行為時, 我們可以繼承一個 ValueFormatAttribute 來覆寫行為.
class Base64ValueFormatAttribute:ValueFormatAttribute{
public override string WriteValueFormat(object value,Type type, JsonSerializerHandler handler, out bool isValueFormat)
{
isValueFormat=true;
if(value==null)
return "null";
else
return ConvertToBase64((byte[])value);
}
public override object ReadValueFormat(string value,Type type, JsonDeserializeHandler handler, out bool isValueFormat)
{
isValueFormat=true;
if(value=="null")
return null;
else
return Base64Convert(value);
}
}
值格式化特性也可以標(biāo)記在結(jié)構(gòu)體或類上, 而另一點是對于值格式化器, 也可以以全局的方式來進(jìn)行配置:以序列化為例, 可通過 JsonSerializerOption中的GlobalValueFormat委托來進(jìn)行配置
JsonSerializerOption.GlobalValueFormat=(value,type,handler,isValueFormat)=>{
if(type==typeof(byte[]))
{
isValueFormat=true;
if(value==null)
return "null";
else
return ConvertToBase64((byte[])value);
}
else
{
isValueFormat=false;
return null;
}
}
值得注意的是,對于byte[]類型的base64解析行為, KoobooJson已經(jīng)內(nèi)嵌在配置項中, 只要設(shè)置JsonSerializerOption.IsByteArrayFormatBase64=true即可
全局Key格式化
對于Model中的Key處理, KoobooJson支持全局的Key格式化器.
class R01_User{
public string R01_Name;
public int R01_Age;
}
如果我們想把R01這個前綴給去掉, 只需要注冊全局Key格式化器的委托即可
JsonSerializerOption.GlobalKeyFormat=(key,parentType,handler)=>{
if(parentType==typeof(R01_User))
{
return key.Substring(4);
}
return key;
}
這樣,出來的json是這樣的:{"Name":"","Age":""
同樣, 對于反序列化,我們也同樣應(yīng)該注冊:
JsonDeserializeOption.GlobalKeyFormat=(key,parentType)=>{
if(parentType==typeof(R01_User))
{
return "R01_"+key;
}
return key;
}
?
原文地址:https://www.cnblogs.com/1996V/p/10607916.html
.NET社區(qū)新聞,深度好文,歡迎訪問公眾號文章匯總 http://www.csharpkit.com
總結(jié)
以上是生活随笔為你收集整理的开源 , KoobooJson一款高性能且轻量的JSON框架的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Kubernetes架构为什么是这样的?
- 下一篇: 分布式系统的构建原则