json 取值判断_【收藏级】.NETCore3.1中的Json互操作解读
本文將會(huì)全面介紹System.Text.Json 和 Newtonsoft.Json 的相同和異同之處,方便需要的同學(xué)做遷移使用,對(duì)未來,我們保持期待。
文檔比較
幾個(gè)重要的對(duì)象
在 System.Text.Json 中,有幾個(gè)重量級(jí)的對(duì)象,所有的JSON互操作,都是圍繞這幾個(gè)對(duì)象進(jìn)行,只要理解了他們各自的用途用法,就基本上掌握了JSON和實(shí)體對(duì)象的互操作。
JsonDocument
提供用于檢查 JSON 值的結(jié)構(gòu)內(nèi)容,而不自動(dòng)實(shí)例化數(shù)據(jù)值的機(jī)制。JsonDocument 有一個(gè)屬性 RootElement,提供對(duì)JSON文檔根元素的訪問,RootElement是一個(gè)JsonElement對(duì)象。
JsonElement
提供對(duì)JSON值的訪問,在System.Text.Json 中,大到一個(gè)對(duì)象、數(shù)組,小到一個(gè)屬性、值,都可以通過 JsonElement 進(jìn)行互操作
JsonProperty
JSON中最小的單元,提供對(duì)屬性、值的訪問
JsonSerializer
提供JSON互操作的靜態(tài)類,提供了一系列 Serializer/Deserialize 的互操作的方法,其中還有一些異步/流式操作方法。
JsonSerializerOptions
與上面的 JsonSerializer 配合使用,提供自定義的個(gè)性化操作選項(xiàng),包括命名、枚舉轉(zhuǎn)換、字符轉(zhuǎn)義、注釋規(guī)則、自定義轉(zhuǎn)換器等等操作選項(xiàng)。
Utf8JsonWriter/Utf8JsonReader
這兩個(gè)對(duì)象是整個(gè) System.Text.Json 的核心對(duì)象,所有的JSON互操作幾乎都是通過這兩個(gè)對(duì)象進(jìn)行,他們提供的高性能的底層讀寫操作。
初始化一個(gè)簡(jiǎn)單的 JSON 對(duì)象
在 System.Text.Json 中,并未提供像 JToken 那樣非常便捷的創(chuàng)建對(duì)象的操作,想要?jiǎng)?chuàng)建一個(gè) JSON 對(duì)象,其過程是比較麻煩的,請(qǐng)看下面的代碼,進(jìn)行對(duì)比
System.Text.Json 的操作便利性在這方面目前處于一個(gè)比較弱的狀態(tài),不過,從這里也可以看出,可能官方并不希望我們?nèi)ブ苯硬僮?JSON 源,而是通過操作實(shí)體對(duì)象以達(dá)到操作 JSON 的目的,也可能對(duì)互操作是性能比較自信的表現(xiàn)吧。
封裝和加載
在對(duì)JSON文檔進(jìn)行包裝的用法
我發(fā)現(xiàn)MS這幫人很喜歡使用 Document 這個(gè)詞,包括XmlDocument/XDocument等等。
查找元素(對(duì)象)
你看,到查找元素環(huán)節(jié)就體現(xiàn)出差異了,JsonDocuemnt 索引僅支持 Array 類型的JSON文檔,而 JToken 則支持 object 類型的索引(充滿想象),用戶體驗(yàn)高下立判。那我們不禁要提問了,如何在 JsonDocument 中查找元素?答案如下。
從上面的代碼來看,JsonElement 存在兩個(gè)迭代器,分別是EnumerateArray和EnumerateObject;通過迭代器,你可以實(shí)現(xiàn)查找元素的需求。你看,MS關(guān)上了一扇門,然后又為了打開了一扇窗,還是很人性化的了。在System.Text.Json中,一切對(duì)象都是Element,Object/Array/Property,都是Element,這個(gè)概念和XML一致,但是和Newtonsoft.Json不同,這是需要注意的地方。
你也可以選擇不迭代,直接獲取對(duì)象的屬性,比如使用下面的方法
上面這段代碼將拋出異常,因?yàn)閷傩?age 不存在,通常情況下,我們會(huì)立即想用一個(gè) ContainsKey 來作一個(gè)判斷,但是很可惜,JsonElement 并未提供該方法,而是提供了一個(gè) TryGetProperty 方法;所以,除非你明確知道 json 對(duì)象中的屬性,否則一般情況下,建議使用 TryGetProperty 進(jìn)行取值。
就算是這樣,使用 GetProperty/TryGetProperty 得到的值,還是一個(gè) JsonElement 對(duì)象,并不是你期望的“值”。所以 JsonElement 很人性化的提供了各種 GetIntxx/GetString 方法,但是就算如此,還是可能產(chǎn)生意外,思考下面的代碼:
上面的代碼,最后一行將拋出異常,因?yàn)槟銍L試從一個(gè) null 到 int32 的類型轉(zhuǎn)換,怎么解決這種問題呢,又回到了 JsonElement 上面來,他又提供了一個(gè)對(duì)值進(jìn)行檢查的方法。
這個(gè)時(shí)候,程序運(yùn)行良好,JsonValueKind 枚舉提供了一系列的類型標(biāo)識(shí),為了進(jìn)一步縮小內(nèi)存使用率,Json團(tuán)隊(duì)用心良苦的將枚舉值聲明為:byte 類型(夠摳)
看到這里,你是不是有點(diǎn)想念 Newtonsoft.Json 了呢?別著急,下面我給大家介紹一個(gè)寶貝 System.Json.dll。
System.Json
基本介紹
System.Json 提供了對(duì)JSON 對(duì)象序列化的基礎(chǔ)支持,但是也是有限的支持,請(qǐng)看下圖
System.Json 目前已合并到 .NETCore-3.1 中,如果你希望使用他,需要單獨(dú)引用
這個(gè)JSON操作包提供了幾個(gè)常用的操作類型,從下面的操作類不難看出,提供的支持是非常有限的,而且效率上也不好說
首先,JsonObject是實(shí)現(xiàn) IDictionary 接口,并在內(nèi)部維護(hù)一個(gè) SortedDictionary字典,所以他具備字典類的一切操作,比如索引等等,JsonArray 就更簡(jiǎn)單,也是一樣的實(shí)現(xiàn) IList接口,然后同樣的在內(nèi)部維護(hù)一個(gè) List鏈表,以實(shí)現(xiàn)數(shù)組功能,對(duì)象的序列化都是通過 JsonValue 進(jìn)行操作,序列化的方式也是非常的簡(jiǎn)單,就是對(duì)對(duì)像進(jìn)行迭代,唯一值得稱道的地方是,采用了流式處理。
使用System.Json操作上面的查找過程如下
令人遺憾的是,雖然 System.Json 已經(jīng)合并到 .NETCore-3.1 的路線圖中;但是,System.Text.Json 不提供對(duì) System.Json 的互操作性,我們期待以后 System.Text.Json 也能提供 System.Json 的操作便利性。
序列化和反序列化
基本知識(shí)已經(jīng)介紹完成,下面我們進(jìn)入 System.Text.Json 的內(nèi)部世界一探究竟。
互操作
思考下面的代碼
目前為止,上面的代碼工作良好。讓我們對(duì)上面的代碼稍作修改,將 JSON 字符串進(jìn)行一個(gè)轉(zhuǎn)小寫的操作后再進(jìn)行反序列化的操作
上面的代碼可以正常運(yùn)行,也不會(huì)拋出異常,你可以得到一個(gè)完整的 user 對(duì)象;但是,user對(duì)象的屬性值將會(huì)丟失!這是因?yàn)?System.Text.Json 默認(rèn)采用的是區(qū)分大小寫匹配的方式,為了解決這個(gè)問題,我們需要引入序列化操作個(gè)性化設(shè)置,請(qǐng)參考下面的代碼,啟用忽略大小寫的設(shè)置
格式化JSON
現(xiàn)在你可以選擇對(duì)序列化的JSON文本進(jìn)行美化,而不是輸出上面的壓縮后的JSON文本,為了實(shí)現(xiàn)美化的效果,你僅僅需要在序列化的時(shí)候加入一個(gè) WriteIndented 設(shè)置
你看,就是這么簡(jiǎn)單,但是你也發(fā)現(xiàn)了,上面的 Remark 屬性在序列化后,中文被轉(zhuǎn)義了,這就是接下來要解決的問題
字符轉(zhuǎn)義的問題
在默認(rèn)情況下,System.Text.Json 序列化程序?qū)λ蟹?ASCII 字符進(jìn)行轉(zhuǎn)義;這就是中文被轉(zhuǎn)義的根本原因。但是在內(nèi)部,他又允許你自定義控制字符集的轉(zhuǎn)義行為,這個(gè)設(shè)置就是:Encoder,比如下面的代碼,對(duì)中文進(jìn)行轉(zhuǎn)義的例外設(shè)置,需要?jiǎng)?chuàng)建一個(gè) TextEncoderSettings 對(duì)象,并將 UnicodeRanges.All 加入允許例外范圍內(nèi),并使用 JavaScriptEncoder 根據(jù) TextEncoderSettings創(chuàng)建一個(gè) JavaScriptEncoder 對(duì)象即可。
還有另外一種模式,可以不必設(shè)置例外而達(dá)到不轉(zhuǎn)義的效果,這個(gè)模式就是“非嚴(yán)格JSON”模式,將上面的 JavaScriptEncoder.Create(encoderSettings) 替換為下面的代碼
序列化相關(guān)-異步/流式
System.Text.Josn 提供了一系列豐富的JSON互操作,這其中包含異步和流式處理,這點(diǎn)也是和 Newtonsoft.Json 最大的不同,但不管是那種方式,都要牢記,最后都是通過下面的兩個(gè)類來實(shí)現(xiàn)
自定義 JSON 名稱和值
在默認(rèn)情況下,輸出的JSON屬性名稱保持和實(shí)體對(duì)象相同,包括大小寫的都是一致的,枚舉類型在默認(rèn)情況下被序列化為數(shù)值類型。System.Text.JSON 提供了一系列的設(shè)置和擴(kuò)展來幫助開發(fā)者實(shí)現(xiàn)各種自定義的需求。下面的代碼可以設(shè)置默認(rèn)的JSON屬性名稱,這個(gè)設(shè)置和 Newtonsoft.Json 基本一致。
UserInfo 的 屬性 Name 在輸出為 JSON 的時(shí)候,其字段名稱將為:name,其他屬性保持大小寫不變
對(duì)所有屬性設(shè)置為 camel 大小寫
自定義名稱策略
比如我們的系統(tǒng),目前采用全小寫的模式,那么我可以自定義一個(gè)轉(zhuǎn)換器,并應(yīng)用到序列化行為中。
將枚舉序列化為名稱字符串而不是數(shù)值
排除不需要序列化的屬性
在默認(rèn)情況下,所有公共屬性將被序列化為JSON。但是,如果你不想讓某些屬性出現(xiàn)在 JSON 中,可以通過下面的幾種方式實(shí)現(xiàn)屬性排除
排除所有屬性值為 null 屬性
排除指定標(biāo)記屬性
可以為某個(gè)屬性應(yīng)用 JsonIgnore 特性,標(biāo)記為不輸出到 JSON
排除所有只讀屬性
還可以選擇對(duì)所有只讀屬性進(jìn)行排查輸出 JSON,比如下面的代碼,Password 是不需要輸出的,那么我們只需要將 Password 設(shè)置為 getter,并應(yīng)用 IgnoreReadOnlyProperties = true 即可
排除派生類的屬性
在某些情況下,由于業(yè)務(wù)需求的不同,需要實(shí)現(xiàn)實(shí)體對(duì)象的繼承,但是在輸出 JSON 的時(shí)候,希望只輸出基類的屬性,而不要輸出派生類型的屬性,以避免產(chǎn)生不可控制的數(shù)據(jù)泄露問題;那么,我們可以采用下面的序列化設(shè)置。比如下面的 UserInfoExtension 派生自 UserInfo,并擴(kuò)展了一個(gè)屬性為身份證的屬性,在輸出 JSON 的時(shí)候,我們希望不要序列化派生類,那么我們可以在 Serialize 序列化的時(shí)候,指定序列化的類型為基類:UserInfo,即可達(dá)到隱藏派生類屬性的目的。
僅輸出指定屬性(排除屬性的逆向操作)
在 Newtonsoft.Json 中,我們可以通過指定 MemberSerialization 和 JsonProperty 來實(shí)現(xiàn)輸出指定屬性到 JSON 中,比如下面的代碼
不過,很遺憾的告訴大家,目前 System.Text.Json 不支持這種方式;為此,我特意去看了 corefx 的 issue,我看到了下面這個(gè)反饋
當(dāng) .NETCore 合并到主分支 .NET 也就是 .NET5.0 的時(shí)候,官方將提供支持,在此之前,還是使用推薦 Newtonsoft.Json 。
在反序列化的時(shí)候,允許 JSON 文本包含注釋
默認(rèn)情況下,System.Text.JSON 不支持源JSON 文本包含注釋,比如下面的代碼,當(dāng)你不使用 ReadCommentHandling = JsonCommentHandling.Skip 的設(shè)置的時(shí)候,將拋出異常,因?yàn)樵谧侄?Age 的后面有注釋 /* age */。
允許字段溢出
在接口數(shù)據(jù)出現(xiàn)變動(dòng)時(shí),極有可能出現(xiàn)源 JSON 文本和實(shí)體對(duì)象屬性不匹配的問題,JSON 中可能會(huì)多出一些實(shí)體對(duì)象不存在的屬性,這種情況我們稱之為“溢出”,在默認(rèn)情況下,溢出的屬性將被忽略,如果希望捕獲這些“溢出”的屬性,可以在實(shí)體對(duì)象中聲明一個(gè)類型為:Dictionary的屬性,并對(duì)其應(yīng)用特性標(biāo)記:JsonExtensionData。
為了演示這種特殊的處理,我們聲明了一個(gè)實(shí)體對(duì)象 UserInfo,并構(gòu)造了一個(gè) JSON 源,該 JSON 源包含了一個(gè) UserInfo 不存在的屬性:Money,預(yù)期該 Money 屬性將被反序列化到屬性 ExtensionData 中。
有意思的是,被特性 JsonExtensionData 標(biāo)記的屬性,在序列化為 JSON 的時(shí)候,他又會(huì)將 ExtensionData 的字典都序列化為單個(gè) JSON 的屬性,這里不再演示,留給大家去體驗(yàn)。
轉(zhuǎn)換器
System.Text.Json 內(nèi)置了各種豐富的類型轉(zhuǎn)換器,這些默認(rèn)的轉(zhuǎn)換器在程序初始化 JsonSerializerOptions 的時(shí)候就默認(rèn)加載,在 JsonSerializerOptions 內(nèi)部,維護(hù)著一個(gè)私有靜態(tài)成員 s_defaultSimpleConverters,同時(shí)還有一個(gè)公有屬性 Converters ,Converters 屬性在 JsonSerializerOptions 的構(gòu)造函數(shù)中被初始化;從下面的代碼中可以看到,默認(rèn)轉(zhuǎn)換器集合和公有轉(zhuǎn)換器集是相互獨(dú)立的,System.Text.Json 允許開發(fā)人員通過 Converters 添加自定義的轉(zhuǎn)換器。
內(nèi)置轉(zhuǎn)換器
在 System.Text.Json 內(nèi)置的轉(zhuǎn)換器集合中,涵蓋了所有的基礎(chǔ)數(shù)據(jù)類型,這些轉(zhuǎn)換器的設(shè)計(jì)非常精妙,他們通過注冊(cè)一系列的類型映射,在通過 Utf8JsonWriter/Utf8JsonReader 的內(nèi)置方法 GetTypeValue/TryGetTypeValue 方法得到值,代碼非常精練,復(fù)用性非常高,下面是內(nèi)置類型轉(zhuǎn)換器。
自定義類型轉(zhuǎn)換器
雖然 System.Text.Json 內(nèi)置了各種各樣豐富的類型轉(zhuǎn)換器,但是在各種業(yè)務(wù)開發(fā)的過程中,總會(huì)根據(jù)業(yè)務(wù)需求來決定一些特殊的數(shù)據(jù)類型的數(shù)據(jù),下面,我們就以經(jīng)典的日期/時(shí)間轉(zhuǎn)換作為演示場(chǎng)景。
我們需要將日期類型輸出為 Unix 時(shí)間戳而不是格式化的日期內(nèi)容,為此,我們將實(shí)現(xiàn)一個(gè)自定義的時(shí)間格式轉(zhuǎn)換器,該轉(zhuǎn)換器繼承自 JsonConverter。
應(yīng)用自定義的時(shí)間轉(zhuǎn)換器
轉(zhuǎn)換器的應(yīng)用形式有兩種,分別是將轉(zhuǎn)換加入 JsonSerializerOptions.Converters 和給需要轉(zhuǎn)換的屬性添加特性標(biāo)記 JsonConverter
加入Converters 方式
應(yīng)用 JsonConverter 特性方式
注意上面的 UserInfo.LoginTime 的特性標(biāo)記,當(dāng)你想小范圍的對(duì)某些屬性單獨(dú)應(yīng)用轉(zhuǎn)換器的時(shí)候,這種方式費(fèi)用小巧而有效。
結(jié)束語
本文全面的介紹了 System.Text.Json 在各種場(chǎng)景下的用法,并比較和 Newtonsoft.Json 使用上的不同,也通過實(shí)例演示了具體的使用方法,進(jìn)一步深入講解了 System.Text.Json 各種對(duì)象的原理,希望對(duì)大家在遷移到.NETCore-3.1 的時(shí)候有所幫助。
歡迎關(guān)注“新閣上位機(jī)編程”抖音號(hào)
不定期發(fā)布上位機(jī)實(shí)用小技巧哦
快來學(xué)習(xí)互粉呀~
(長(zhǎng)按下方圖片?保存到手機(jī)相冊(cè),打開抖音掃碼關(guān)注哦!相信你肯定會(huì))
總結(jié)
以上是生活随笔為你收集整理的json 取值判断_【收藏级】.NETCore3.1中的Json互操作解读的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 1-2docker-基本的使用
- 下一篇: easyui datagrid java