這個題目是筆者在StackckOverflow上回答問題,後續被其他使用者用評論追問的題目,不過可惜研究出來後,因為解法較複雜,請對方再開一個問題,結果就沒下文了~可能對方已經自己找出答案了吧,為了不浪費,就拿來當這次的題材吧XD
首先介紹一下原題目 How to get list of values by key’s name across Json using C# 題目需求算明確,就是如何一次取得每一層特定屬性名稱的值,JSON範例如下:
{
"objectId": "123",
"properties": {
"objectId": "456"
},
"variables": [
{
"objectId": "789"
},
{
"objectId": "012"
}
]
}
需要取得所有objectId的值,並存在一個陣列中,期望輸出是
["123", "456", "789", "012"]
當初為了解這題也是去Google了一下,後來發現到關鍵方法DescendantsAndSelf可以將本身這一層以及底下每一層元素,算是打掉層數,扁平化到同一層的概念,接著再篩選一下屬性名稱取得值就可以了
var root = (JContainer)JToken.Parse(json);
var list = root.DescendantsAndSelf().OfType<JProperty>()
.Where(p => p.Name == "objectId")
.Select(p => p.Value.Value<string>());
Console.WriteLine(string.Join(",", list.ToArray()));
這邊算是開胃菜,接著有網友詢問如果要看兩個屬性,且需要成對,objectId屬性要對同一層的objectvalue屬性,若缺其一就忽略或者使用null,期望輸出一個二維陣列,但我覺得做成Dictionary應該會比較適合
{
"objectId": "123",
"properties": {
"objectId": "456",
"objectvalue": "aaa"
},
"variables": [
{
"objectId": "789",
"objectvalue": "bbb"
},
{
"objectId": "012",
"objectvalue": "ccc"
}
]
}
看起來沒辦法像之前用遍平化的方式處理,因為需要在不同層各判斷是否有成對的屬性,因此決定將每一層歷遍,同時再判斷屬性是否成對,同值將值寫入Dictionary中,解法如下:
private static Dictionary<string, string> result = new Dictionary<string, string>();
private static void Main(string[] args)
{
var json = "{\"objectId\":\"123\",\"properties\":{\"objectId\":\"456\",\"objectvalue\":\"aaa\"},\"variables\":[{\"objectId\":\"789\",\"objectvalue\":\"bbb\"},{\"objectId\":\"012\",\"objectvalue\":\"ccc\"}]}";
var root = JObject.Parse(json);
//取得這一層所有的屬性
var propertyList = root.Properties();
handleIdValue(propertyList);
Console.WriteLine("Keys: " + string.Join(",", result.Keys)); //456, 789, 012
Console.WriteLine("Values: " + string.Join(",", result.Values)); //aaa, bbb, ccc
}
private static void handleIdValue(IEnumerable<JProperty> propertyList)
{
//依據每一層判斷是否有成對的屬性,再依照屬性型別深入,確定成對後寫入result中
var haveId = false;
var id = "";
foreach (var property in propertyList)
{
//若值是字串,判斷成對名稱
if (property.Value.Type == JTokenType.String)
{
if (property.Name == "objectId")
{
id = property.Value.Value<string>();
haveId = true;
}
if (haveId && property.Name == "objectvalue")
{
result.Add(id, property.Value.Value<string>());
}
}
//若是陣列,依序將每一個物件取得屬性,遞迴處理
else if (property.Value.Type == JTokenType.Array)
{
var array = property.Value.Value<JArray>();
foreach (var p in array)
{
if (p.Type == JTokenType.Object)
{
handleIdValue(p.Value<JObject>().Properties());
}
}
}
//若是物件,取得屬性,遞迴處理
else if (property.Value.Type == JTokenType.Object)
{
handleIdValue(property.Value.Value<JObject>().Properties());
}
}
}
依照這個解法可以達到題目的要求,不過陣列那邊,如果有多維陣列的話,就需要將處理陣列的部分也分離一個functiont出來,同樣用遞迴處理。藉由這次的解題,也讓我對JSON.NET有更深入的認識,平常頂多就是序列化跟反序列化而已,很少需要客製的分析JSON字串
每次在解stackoverflow的題目時,都還蠻緊張的,因為新題目大家都在搶,稍微慢一點,可能就已經選出答案了,但後來比較釋懷了,好的答案比較重要,也有長尾效應,如果真的有幫助,最後還是會獲得不少認同分數的~
發表迴響