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