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

一:背景

1. 講故事

前段時間和一家公司聯調api接口的時候,發現一個奇葩的問題,它的api返回的json會動態改變,簡化以下:php

{"Code":101,"Items":[{"OrderTitle":"訂單1"}]}

{"Code":102,"Items":[{"ProductTitle":"商品1"}]}

邏輯是這樣的: Items 中的內容會隨的 Code 的改變而改變,裏面有多是訂單列表又有多是商品列表,習慣弱類型的朋友看這種json太正常不過了,但對於強類型的咱們來講,簡直就是一個大寫的奇葩,你這讓我用什麼強類型反序列化呢???,若是還沒理解,請看下面的這張圖吧!json

通過溝通,對方果真用的是弱類型的php,磨了半天,說服讓對方改了返回結構,這樣就能夠直接用固有類匹配。api

二:尋找解決辦法

從業務上來講,能說服對方讓步那是最好的,但從技術上來講,這種場景有什麼好的解決辦法呢? 問題的本質就是json是動態的,你反序列化的時候沒法指定匹配類。app

1. 使用 dynamic

既然是動態的,那C#中也有一個動態類型 dynamic,何不用它來作json中動態變化的那部分的接受值,將 items 定義爲 dynamic。以下圖:jsonp

從圖中看: rsp.Items as List<OrderItem> 返回是null,嘗試失敗,雖然轉化失敗了,但我相信你也看到了 Newtonsoft.Json.Linq.JArray,貌似這玩意能夠用 linq 操控,對的, 這就是 linq to json.net

2. 使用 linq to json

有了linq基礎,提取JArray中內容就不難了,接下來把代碼改爲以下:code

static void Main(string[] args)
        {
            var json = "{\"Code\":101,\"Items\":[{\"OrderTitle\":\"訂單1\"}]}";

            var rsp = JsonConvert.DeserializeObject<ApiResponse>(json);

            if (rsp.Code == 101)
            {
                var items = (rsp.Items as JArray).Select(m => m["OrderTitle"].Value<string>()).ToList();

                Console.WriteLine(string.Join(",", items));
            }

            if (rsp.Code == 102)
            {
                //todo ....
            }
        }

從代碼中能夠看到,我是經過code的不一樣作了不一樣的業務邏輯處理,貌似問題經過這種半自動化的model實現了,但擁有強大好奇心的你,豈能不往下挖?xml

三: linq to json 分析

1. 好處

我以爲 linq to json 的最大好處就是繞過了強類型限制,能夠像弱類型語言同樣處理生成和讀取json,給了咱們在業務處理上更多的選擇餘地,接下來我就在Create和Query上給你們拋磚引玉吧。blog

2. 生成json

在沒有強類型的狀況下,如何構建json結構呢? 對了,不知道你們對 linq to xml 還有熟悉的嗎? 還記得它是怎麼一步一步構建的哈,若是你記得的話,這裏也是差很少的構建方式,好比說剛纔的 JArray。接口

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());

從圖中看這種手工構建json的方式仍是比較繁瑣的,走的就是 linq to xml 的路子,有沒有更簡單的方式呢? 我以爲這裏你能夠用 C# 中的一個語法糖:匿名類型,雖然從 IL 上看也是強類型,但在用在這裏太合適了,接下來我來改造一下:

JObject json = JObject.FromObject(new
            {
                Code = 101,
                Items = (new[]
                {
                    new { OrderTitle="訂單1",Created=DateTime.Now }
                }).ToList()
            });

            Console.WriteLine(json.ToString());

這樣是否是太方便了,算是巧用 匿名類型 吧。

2. 解析json

爲了讓結果更可觀,我準備生成一個稍微複雜一點的json,而後經過 linq to jsonjsonpath 兩種方式操控json。

{
    "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 進行分組,統計每一個類別的總金額
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<decimal>("price")).Sum());

            foreach (var key in dict.Keys)
            {
                Console.WriteLine($"key={key},value={dict[key]}");
            }
        }

哈哈,分組統計在強大的linq面前就是這麼簡單!

  • 使用 jsonpath 處理

jsonpath 就像 xmlpath 同樣,很是強大,更多的功能能夠參考這個網頁: https://goessner.net/articles/JsonPath/。

根據上面的語法,我嘗試着提取全部的price,使用 $..price 試試。

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<decimal>());
            }

四: 總結

我相信你們在90%的狀況都是用強類型做爲json的mapping,剩下的10%狀況,能夠了解下強大的 linq to json哈,太實用啦! 但願本篇對您有幫助。

如您有更多問題與我互動,掃描下方進來吧~

圖片名稱
相關文章
相關標籤/搜索