Newtonsoft 六個超簡單又實用的特性,值得一試 【下篇】

一:講故事

上一篇介紹的 6 個特性從園子裏的反饋來看效果不錯,那這一篇就再帶來 6 個特性同你們一塊兒欣賞。php

二:特性分析

1. 像弱類型語言同樣解析 json

你們都知道弱類型的語言有不少,如: nodejs,python,php,它們有一個🐮👃的地方就是處理json,不須要像 強類型語言 那樣還要給它配一個類,什麼意思呢? 就拿下面的 json 說事。node

{
  "DisplayName": "新一代算法模型",
  "CustomerType": 1,
  "Report": {
    "TotalCustomerCount": 1000,
    "TotalTradeCount": 50
  },
  "CustomerIDHash": [1,2,3,4,5]
}

這個 json 若是想灌到 C# 中處理,你就得給它定義一個適配的類,就如 初篇 的客戶算法模型類,因此這裏就有了一個需求,能不能不定義類也能夠自由解析上面這串 json 呢??? 哈哈,固然是能夠的, 反序列化成 Dictionary 便可,就拿提取 Report.TotalCustomerCountCustomerIDHash 這兩個字段演示一下。python

static void Main(string[] args)
        {
            var json = @"{
                           'DisplayName': '新一代算法模型',
                           'CustomerType': 1,
                           'Report': {
                             'TotalCustomerCount': 1000,
                             'TotalTradeCount': 50
                           },
                           'CustomerIDHash': [1,2,3,4,5]
                         }";

            var dict = JsonConvert.DeserializeObject<Dictionary<object, object>>(json);

            var report = dict["Report"] as JObject;
            var totalCustomerCount = report["TotalCustomerCount"];

            Console.WriteLine($"totalCustomerCount={totalCustomerCount}");

            var arr = dict["CustomerIDHash"] as JArray;
            var list = arr.Select(m => m.Value<int>()).ToList();

            Console.WriteLine($"list={string.Join(",", list)}");
        }

2. 如何讓json中的枚舉保持更易讀的字符串型

這句話是什麼意思呢? 默認狀況下, SerializeObject 會將 Model 中的 Enum 變成數值型,你們都知道數值型語義性是很是差的,以下代碼所示:算法

static void Main(string[] args)
    {
        var model = new ThreadModel() { ThreadStateEnum = System.Threading.ThreadState.Running };

        var json = JsonConvert.SerializeObject(model);

        Console.WriteLine(json);
    }

    class ThreadModel
    {
        public System.Threading.ThreadState ThreadStateEnum { get; set; }
    }

對吧,確實語義特別差,那能不能直接生成 Running 這種字符串形式呢? 固然能夠了。。。改造以下:json

var json = JsonConvert.SerializeObject(model, new StringEnumConverter());

這裏可能就有人鑽牛角尖了,能不能部分指定讓枚舉生成 string,其餘的生成 int ,不要緊,這也難不倒我,哪裏使用就用 JsonConverter 標記哪裏。。。ide

static void Main(string[] args)
        {
            var model = new ThreadModel()
            {
                ThreadStateEnum = System.Threading.ThreadState.Running,
                TaskStatusEnum = TaskStatus.RanToCompletion
            };

            var json = JsonConvert.SerializeObject(model);

            Console.WriteLine(json);
        }

        class ThreadModel
        {
            public System.Threading.ThreadState ThreadStateEnum { get; set; }

            [JsonConverter(typeof(StringEnumConverter))]
            public TaskStatus TaskStatusEnum { get; set; }
        }

3. 格式化 json 中的時間類型

在 model 轉化成 json 的過程當中,總少不了 時間類型,爲了讓時間類型 可讀性更高,一般會 格式化爲 YYYY年/MM月/dd日 ,那如何實現呢? 很簡單撒,在 JsonConvert 中也是一個 枚舉 幫你搞定。。。3d

static void Main(string[] args)
        {
            var json = JsonConvert.SerializeObject(new Order()
            {
                OrderTitle = "女裝大佬",
                Created = DateTime.Now
            }, new JsonSerializerSettings
            {
                DateFormatString = "yyyy年/MM月/dd日",
            });

            Console.WriteLine(json);
        }
        public class Order
        {
            public string OrderTitle { get; set; }
            public DateTime Created { get; set; }
        }

對了,我記得很早的時候,C# 自帶了一個 JavaScriptSerializer, 也是用來進行 model 轉 json的,可是它會將 datetime 轉成 時間戳,而不是時間字符串形式,若是你由於特殊緣由想經過 JsonConvert 將時間生成時間戳的話,也是能夠的, 用 DateFormatHandling.MicrosoftDateFormat 枚舉指定一下便可,以下:日誌

4. 對一些經常使用設置進行全局化

在以前全部演示的特性技巧中都是在 JsonConvert 上指定的,也就是說 100 個 JsonConvert 我就要指定 100 次,那有沒有相似一次指定,整個進程通用呢? 這麼強大的 Newtonsoft 早就支持啦, 就拿上面的 Order 舉例:code

JsonConvert.DefaultSettings = () =>
        {
            var settings = new JsonSerializerSettings
            {
                Formatting = Formatting.Indented
            };
            return settings;
        };

        var order = new Order() { OrderTitle = "女裝大佬", Created = DateTime.Now };

        var json1 = JsonConvert.SerializeObject(order);
        var json2 = JsonConvert.SerializeObject(order);

        Console.WriteLine(json1);
        Console.WriteLine(json2);

能夠看到,Formatting.Indented 對兩串 json 都生效了。orm

5. 如何保證 json 到 model 的嚴謹性 及提取 json 未知字段

有時候咱們有這樣的需求,一旦 json 中出現 model 未知的字段,有兩種選擇: 要麼報錯,要麼提取出未知字段,在 Newtonsoft 中默認的狀況是忽略,場景你們能夠本身找哈。

  • 未知字段報錯
static void Main(string[] args)
        {
            var json = "{'OrderTitle':'女裝大佬', 'Created':'2020/6/23','Memo':'訂單備註'}";

            var order = JsonConvert.DeserializeObject<Order>(json, new JsonSerializerSettings
            {
                MissingMemberHandling = MissingMemberHandling.Error
            });

            Console.WriteLine(order);
        }

        public class Order
        {
            public string OrderTitle { get; set; }
            public DateTime Created { get; set; }
            public override string ToString()
            {
                return $"OrderTitle={OrderTitle}, Created={Created}";
            }
        }

  • 提取未知字段

我依稀的記得 WCF 在這種場景下也是使用一個 ExtenstionDataObject 來存儲客戶端傳過來的未知字段,有多是客戶端的 model 已更新,server端仍是舊版本,一般在 json 序列化中也會遇到這種狀況,這裏只要使用 JsonExtensionData 特性就能夠幫你搞定,在 OnDeserialized 這種AOP方法中進行攔截,以下代碼:

static void Main(string[] args)
    {
        var json = "{'OrderTitle':'女裝大佬', 'Created':'2020/6/23','Memo':'訂單備註'}";

        var order = JsonConvert.DeserializeObject<Order>(json);

        Console.WriteLine(order);
    }

    public class Order
    {
        public string OrderTitle { get; set; }

        public DateTime Created { get; set; }

        [JsonExtensionData]
        private IDictionary<string, JToken> _additionalData;

        public Order()
        {
            _additionalData = new Dictionary<string, JToken>();
        }

        [OnDeserialized]
        private void OnDeserialized(StreamingContext context)
        {
            var dict = _additionalData;
        }

        public override string ToString()
        {
            return $"OrderTitle={OrderTitle}, Created={Created}";
        }
    }

6. 開啓 JsonConvert 詳細日誌功能

有時候在查閱源碼的時候開啓日誌功能更加有利於理解源碼的內部運做,因此這也是一個很是實用的功能,看看如何配置吧。

static void Main(string[] args)
        {
            var json = "{'OrderTitle':'女裝大佬', 'Created':'2020/6/23','Memo':'訂單備註'}";

            MemoryTraceWriter traceWriter = new MemoryTraceWriter();

            var account = JsonConvert.DeserializeObject<Order>(json, new JsonSerializerSettings
            {
                TraceWriter = traceWriter
            });

            Console.WriteLine(traceWriter.ToString());
        }

        public class Order
        {
            public string OrderTitle { get; set; }

            public DateTime Created { get; set; }

            public override string ToString()
            {
                return $"OrderTitle={OrderTitle}, Created={Created}";
            }
        }

三:總結

嘿嘿,這篇 6 個特性就算說完了, 結合上一篇一共 12 個特性,是否是很是簡單且實用,後面準備給你們帶來一些源碼解讀吧! 但願本篇對您有幫助,謝謝!

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

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