1. 程式人生 > >api介面返回動態的json格式?我太難了,嘗試一下 linq to json

api介面返回動態的json格式?我太難了,嘗試一下 linq to json

## 一:背景 ### 1. 講故事 前段時間和一家公司聯調api介面的時候,發現一個奇葩的問題,它的api返回的json會動態改變,簡化如下: ``` json {"Code":101,"Items":[{"OrderTitle":"訂單1"}]} {"Code":102,"Items":[{"ProductTitle":"商品1"}]} ``` 邏輯是這樣的: `Items` 中的內容會隨的 Code 的改變而改變,裡面有可能是訂單列表又有可能是商品列表,習慣弱型別的朋友看這種json太正常不過了,但對於強型別的我們來說,簡直就是一個大寫的奇葩,你這讓我用什麼強型別反序列化呢???,如果還沒理解,請看下面的這張圖吧! ![](https://img2020.cnblogs.com/other/214741/202008/214741-20200803093408368-1154053431.png) 經過溝通,對方果然用的是弱型別的php,磨了半天,說服讓對方改了返回結構,這樣就可以直接用固有類匹配。 ## 二:尋找解決辦法 從業務上來說,能說服對方讓步那是最好的,但從技術上來說,這種場景有什麼好的解決辦法呢? 問題的本質就是json是動態的,你反序列化的時候無法指定匹配類。 ### 1. 使用 dynamic 既然是動態的,那C#中也有一個動態型別 dynamic,何不用它來做json中動態變化的那部分的接受值,將 `items` 定義為 dynamic。如下圖: ![](https://img2020.cnblogs.com/other/214741/202008/214741-20200803093408654-1135271174.png) 從圖中看: `rsp.Items as List` 返回是null,嘗試失敗,雖然轉化失敗了,但我相信你也看到了 `Newtonsoft.Json.Linq.JArray`,貌似這玩意可以用 linq 操控,對的, 這就是 `linq to json`。 ### 2. 使用 linq to json 有了linq基礎,提取JArray中內容就不難了,接下來把程式碼改成如下: ``` C# static void Main(string[] args) { var json = "{\"Code\":101,\"Items\":[{\"OrderTitle\":\"訂單1\"}]}"; var rsp = JsonConvert.DeserializeObject(json); if (rsp.Code == 101) { var items = (rsp.Items as JArray).Select(m => m["OrderTitle"].Value()).ToList(); Console.WriteLine(string.Join(",", items)); } if (rsp.Code == 102) { //todo .... } } ``` ![](https://img2020.cnblogs.com/other/214741/202008/214741-20200803093408869-1753391580.png) 從程式碼中可以看到,我是通過code的不同做了不同的業務邏輯處理,貌似問題通過這種半自動化的model實現了,但擁有強大好奇心的你,豈能不往下挖? ## 三: linq to json 分析 ### 1. 好處 我覺得 linq to json 的最大好處就是繞過了強型別限制,可以像弱型別語言一樣處理生成和讀取json,給了我們在業務處理上更多的選擇餘地,接下來我就在Create和Query上給大家拋磚引玉吧。 ### 2. 生成json 在沒有強型別的情況下,如何構建json結構呢? 對了,不知道大家對 `linq to xml` 還有熟悉的嗎? 還記得它是怎麼一步一步構建的哈,如果你記得的話,這裡也是差不多的構建方式,比如說剛才的 JArray。 ``` C# JObject json = new JObject( new JProperty("Code", 101), new JProperty("Items", new JArray(new JObject() { new JProperty("OrderTitle","訂單1"), new JProperty("Created",DateTime.Now) })) ); Console.WriteLine(json.ToString()); ``` ![](https://img2020.cnblogs.com/other/214741/202008/214741-20200803093409063-1700901289.png) 從圖中看這種手工構建json的方式還是比較繁瑣的,走的就是 `linq to xml` 的路子,有沒有更簡單的方式呢? 我覺得這裡你可以用 C# 中的一個語法糖:匿名型別,雖然從 `IL` 上看也是強型別,但在用在這裡太合適了,接下來我來改造一下: ``` C# JObject json = JObject.FromObject(new { Code = 101, Items = (new[] { new { OrderTitle="訂單1",Created=DateTime.Now } }).ToList() }); Console.WriteLine(json.ToString()); ``` ![](https://img2020.cnblogs.com/other/214741/202008/214741-20200803093409207-883788174.png) 這樣是不是太方便了,算是巧用 匿名型別 吧。 ### 2. 解析json 為了讓結果更可觀,我準備生成一個稍微複雜一點的json,然後通過 `linq to json` 和 `jsonpath` 兩種方式操控json。 ``` C# { "store":{ "book":[ { "category":"reference", "author":"Nigel Rees", "title":"Sayings of the Century", "price":8.95 }, { "category":"fiction", "author":"Evelyn Waugh", "title":"Sword of Honour", "price":12.99 }, { "category":"fiction", "author":"Herman Melville", "title":"Moby Dick", "isbn":"0-553-21311-3", "price":8.99 }, { "category":"fiction", "author":"J. R. R. Tolkien", "title":"The Lord of the Rings", "isbn":"0-395-19395-8", "price":22.99 } ], "bicycle":{ "color":"red", "price":19.95 } } } ``` * 對 category 進行分組,統計每個類別的總金額 ``` C# static void Main(string[] args) { var json = System.IO.File.ReadAllText("1.txt"); JObject obj = JObject.Parse(json); var dict = obj["store"]["book"].GroupBy(m => m["category"]) .ToDictionary(k => k.Key, v => v.Select(n => n.Value("price")).Sum()); foreach (var key in dict.Keys) { Console.WriteLine($"key={key},value={dict[key]}"); } } ``` ![](https://img2020.cnblogs.com/other/214741/202008/214741-20200803093409398-21554891.png) 哈哈,分組統計在強大的linq面前就是這麼簡單! * 使用 jsonpath 處理 jsonpath 就像 xmlpath 一樣,非常強大,更多的功能可以參考這個網頁: https://goessner.net/articles/JsonPath/。 ![](https://img2020.cnblogs.com/other/214741/202008/214741-20200803093409661-1045484690.png) 根據上面的語法,我嘗試著提取所有的price,使用 `$..price` 試試。 ``` C# var json = System.IO.File.ReadAllText("1.txt"); JObject obj = JObject.Parse(json); var priceList= obj.SelectTokens("$..price"); foreach (var price in priceList) { Console.WriteLine(price.Value()); } ``` ![](https://img2020.cnblogs.com/other/214741/202008/214741-20200803093409855-873814870.png) ## 四: 總結 我相信大家在90%的情況都是用強型別作為json的mapping,剩下的10%情況,可以瞭解下強大的 linq to json哈,太實用啦! 希望本篇對您有幫助。 ### 如您有更多問題與我互動,掃描下方進來吧~