

.NET Core JSON 轉 Dictionary string, object 地雷
source link: https://blog.darkthread.net/blog/dotnet-json-deserialize-dictionary-issue/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

發現從 .NET 6 開始支援 System.Text.Json DOM 巡覽及編修,小小興奮了一下,打算逐步用 System.Text.Json 取代 Json.NET,不料隨即踩到雷。
有段用 JSON 傳送 Dictionary<string, object> 的程式,原本靠 JsonConvert.DeserializeObject<Dictionary<string, object>>() 將 JSON 轉回 Dictionary<string, object>。開開心心改成 JsonSerializer.Deserializ <Dictionary<string, object>>(),結果在跑測試時爆炸!
用以下程式重現問題:
using System.Text.Json;
var jsonOpt = new JsonSerializerOptions {
WriteIndented = true
};
var dict = new Dictionary<string, object>() {
["i"] = 255,
["s"] = "String",
["d"] = DateTime.Today,
["a"] = new int[] { 1, 2 },
["o"] = new { Prop = 123 }
};
var json = JsonSerializer.Serialize(dict, jsonOpt);
Console.ForegroundColor = ConsoleColor.Cyan;
Console.WriteLine("== JSON ==");
Console.WriteLine(json);
Console.ForegroundColor = ConsoleColor.Green;
Console.WriteLine("== Json.NET ==");
var dJsonNet = Newtonsoft.Json.JsonConvert.DeserializeObject<Dictionary<string, object>>(json);
foreach (var kv in dJsonNet!) {
Console.WriteLine($"{kv.Value.GetType().Name} {kv.Key} = {kv.Value}");
}
Console.ForegroundColor = ConsoleColor.Magenta;
Console.WriteLine("== System.Text.Json ==");
var dSysTextJson = JsonSerializer.Deserialize<Dictionary<string, object>>(json);
foreach (var kv in dSysTextJson!) {
Console.WriteLine($"{kv.Value.GetType().Name} {kv.Key} = {kv.Value}");
}
Console.ResetColor();
問題點為 System.Text.Json 在反序列化 object 時不像 Json.NET 會試著轉型成 int、string、DateTime 等型別(我另外加測了 int[] 及物件,Json.NET 會轉成 JArray 及 JObject),而是一律視為 JsonElement,雖然 JsonElement 提供了 GetByte()、GetGuid()、GetInt32()、GetDateTime()... 等方法,不愁取不出值,但得逐 Key 分別處理,應用上不如 Dictionary<string, object> 便利。
爬文查到有位微軟 MVP 分享自製 DictionaryStringObjectJsonConverter 的解法,透過 JsonConverterAttribute 套用在類別屬性上,原則上可以克服問題。但我心中更理想的解法是未來 System.Text.Json 能內建選項,提供與 Json.NET 類似的轉換邏輯,在此之前,我先試寫一個簡易版擴充函式 ToStringObjectDictionary() 頂著用,順便練手感。
using System.Text.Json.Nodes;
namespace System.Text.Json
{
public static class JsonDictStringObjExtensions
{
public static Dictionary<string, object> ToStringObjectDictionary(this JsonObject jsonObject)
{
var dict = new Dictionary<string, object>();
foreach (var prop in jsonObject)
{
object value;
if (prop.Value == null) value = null!;
else if (prop.Value is JsonArray) value = prop.Value.AsArray();
else if (prop.Value is JsonObject) value = prop.Value.AsObject();
else
{
var v = prop.Value.AsValue();
var t = prop.Value.ToJsonString();
if (t.StartsWith('"')) {
if (v.TryGetValue<DateTime>(out var d)) value = d;
else if (v.TryGetValue<Guid>(out var g)) value = g;
else value = v.GetValue<string>();
}
else value = v.GetValue<decimal>();
}
dict.Add(prop.Key, value);
}
return dict;
}
}
}
修改測試程式,再多測試浮點數、Guid 及 null:
using System.Text.Json;
using System.Text.Json.Nodes;
var jsonOpt = new JsonSerializerOptions {
WriteIndented = true
};
var dict = new Dictionary<string, object>() {
["i"] = 255,
["f"] = 3.1416,
["s"] = "String",
["d"] = DateTime.Today,
["a"] = new int[] { 1, 2 },
["o"] = new { Prop = 123 },
["g"] = Guid.NewGuid(),
["n"] = null!
};
var json = JsonSerializer.Serialize(dict, jsonOpt);
Console.ForegroundColor = ConsoleColor.Cyan;
Console.WriteLine("== JSON ==");
Console.WriteLine(json);
Console.ForegroundColor = ConsoleColor.Green;
Console.WriteLine("== Json.NET ==");
var dJsonNet = Newtonsoft.Json.JsonConvert.DeserializeObject<Dictionary<string, object>>(json);
foreach (var kv in dJsonNet!) {
Console.WriteLine($"{kv.Value?.GetType().Name} {kv.Key} = {kv.Value ?? "null"}");
}
Console.ForegroundColor = ConsoleColor.Magenta;
Console.WriteLine("== System.Text.Json ==");
var dSysTextJson = JsonSerializer.Deserialize<JsonObject>(json)!.ToStringObjectDictionary();
foreach (var kv in dSysTextJson!) {
Console.WriteLine($"{kv.Value?.GetType().Name} {kv.Key} = {kv.Value ?? "null"}");
}
Console.ResetColor();
測試成功!
Recommend
-
51
程序员 - @Persephone - https://github.com/ant-design/ant-design/issues/13098#issuecomment-449754024做框架不好好修 bug,掺 x 为什么这
-
24
程序员 - @ericgui - 比如,那些因此事被裁的,有任何人来做点什么吗?比如,蚂蚁金服有人出来做个什么说明吗?正在为新项目选择 UI 框架,所以需要知道这个事的后续。如果没有后续了,那就算了
-
13
微信8.0 上线,球球你们不要刷屏了。 文章来源:雷科技 ID:leitech 作者:雷科技互联网组 原文编辑丨Wallace 原标题:突发!微信8.0正式上线!等了十年就这? 今天是微信十周年生日 今天是微信十周年生日,当大家都在回顾微信过去十年...
-
9
.NET 彈性日期時間字串解析及小地雷 2021-01-30 08:06 AM 0 655 專案裡有個需求,希望當使用者輸入字串為日期或日期時間時自動轉成 DateTime 型別,我...
-
25
JavaScript 之浮點四捨五入花式地雷 2021-10-08 09:20 PM 0 1,930 同事分享的浮點運算案例。 這些年大家都已知道「
-
11
How to Convert JS Object to JSON String 1090 views 1 year ago Javascript Use the JSON....
-
4
This article demonstrates easy ways to convert a javascript object or an array of objects to JSON String using different methods and example illustrations. Table of Contents: JSON.str...
-
7
[Work with JSON in JS] Convert JSON string object to js object · GitHub Instantly share code, notes, and snippets. [Work with JSON in JS] Co...
-
9
How to convert Java Object to JSON string? advertisements Hello i'm trying to convert my Java Object to a Json...
-
5
How to Turn a C# Object Into a JSON String in .NET?
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK