專案遇到的需求:程式接收來自外界的 JSON 資料,物件之各屬性內容以 KeyValuePair<string, string> 陣列儲存,序列化結果如下:
{
"modType": [
{
"Key": "I",
"Value": "獨立模組"
},
{
"Key": "J",
"Value": "聯合模組"
}
],
"source": [
{
"Key": "I",
"Value": "內部"
},
{
"Key": "E",
"Value": "外部"
}
],
"statusOption": [
{
"Key": "0",
"Value": "停止"
},
{
"Key": "1",
"Value": "運轉"
},
{
"Key": "2",
"Value": "暫停"
}
]
}
若依此資料結構,JavaScript 前端 MVVM 繫結特定項目時要寫成 model.source[1].Value 不夠直覺,希望改成 model.source.E。JavaScript 要將 Key/Value 陣列轉成物件屬性不是難事,jQuery.each() 一行可以搞定:
var obj={};$.map(model.source,function(item){ obj[item.Key]=item.Value;});
不過,傳送繁瑣 JSON 資料到前端再簡化,自然不如在 C# 端直接轉換優雅,而這對 Json.NET 來說是小菜一碟。
程式範例附於下方,簡單說明原理:先將 JSON 字串用 JObject.Parse() 反序列化成 JObject 物件,透過 JObject.Properties() 可逐一取得象徵各屬性的 JProperty 物件,JProperty.Name 為屬性名稱,JProperty.Value 則為屬性值,在本案例為 Key/Value 物件組成的陣列。透過 p.Value as JArray 轉為 JArray 後可 foreach 取得陣列元素。陣列元件可視為包含 Key/Value 兩個屬性的 JObject,可透過 item["Key"]/item["Value"] 取值。我們為每個屬性建立一顆專屬 JObject,將 Key/Value 陣列以 propObj.Add(propName, propValue) 轉成 propObj 的一個個屬性值,藉以取代原有的 JArray,就完成了置換。其中有個小眉角,由於 Key 可能包含不合法的屬性名稱字元或格式(例如「.」字元或以數字起首),因此要藉由 Regex 取代及修正(數字起首時在前方加上「_」)。
staticvoid Main(string[] args)
{
var json = System.IO.File.ReadAllText("data.json");
//將JSON轉為JObject
JObject jo = JObject.Parse(json);
//逐一轉換各屬性
foreach (var p in jo.Properties())
{
//原本Key/Value陣列方式表達選項?容
JArray a = p.Value as JArray;
//準備一個新物件以屬性儲放選項
JObject propObj = new JObject();
//將{ "Key":"..", "Value":"..."}視為JObject
foreach (JObject item in a)
{
string propName = (string)item["Key"]; //取出Key
//將.換成_,數字起首時前方加_,避免產生無效屬性名
propName =
Regex.Replace(
//TODO:如有其他字元再擴充
Regex.Replace(propName, "[-.]", "_"),
"^[0-9]", //若以數字起始前方加_
m => "_" + m.Value);
//取出Value
string propValue = (string)item["Value"];
//新增成屬性
propObj.Add(propName, propValue);
}
jo[p.Name] = propObj;
}
Console.WriteLine(JsonConvert.SerializeObject(jo, Formatting.Indented));
Console.Read();
}
轉換結果如下,成功!
{
"modType": {
"I": "獨立模組",
"J": "聯合模組"
},
"source": {
"I": "內部",
"E": "外部"
},
"statusOption": {
"_0": "停止",
"_1": "運轉",
"_2": "暫停"
}
}
以上寫法展示完 JObject/JProperty/JArray 的概念與應用方式,接著我們來抄捷徑:
JObject jo = JObject.Parse(json);
foreach (var p in jo.Properties())
{
p.Value =
JObject.FromObject(
(p.Value as JArray).Cast<JObject>()
.ToDictionary(
o => {
string propName = (string)o["Key"];
propName =
Regex.Replace(
Regex.Replace(propName, "[-.]", "_"),
"^[0-9]",
m => "_" + m.Value);
return propName;
},
o => (string)o["Value"]));
}
JArray 經由 ToDictionary() 轉成 Dictionary<string, string>,呼叫 JObject.FromObject() 就直接轉成 JObject,收工!
後話:Json.NET 並不是轉換效能最好的 JSON 程式庫,但其完整性、成熟度與應用彈性實在沒話說,只想學一套 JSON 程式庫,選它就對了!
貼文後經網友提醒,補上之前寫過的另一篇介紹:使用dynamic簡化Json.NET JObject操作兩帖併服,藥效加倍~ :P