紀錄一下一個之前跟其他廠商合作的需求,簡單的說因為我們常常對接 API 的時候是透過 JSON
對方不一定是微軟的系統,我們要確定彼此的資料是正確的,很簡單就是直接做 checksum
但是 JSON 這時候第一層的 Propery Name 如果順序不一樣就將無法用同一個方法進行 checksum
這時候就有一個需求,就是將 輸出的 JSON 給排序,只需要第一層的就可以了。

1. 先介紹一下今天要用的物件 User.cs
using System;
using System.Collections.Generic;
using System.Text;
namespace TestXCRSAUtil
{
public class User
{
public string Id { get; set; }
public DateTime Birth { get; set; }
public decimal Salary { get; set; }
public string Memo { get; set; }
public List Friends { get; set; }
public User()
{
Friends = new List();
}
}
}
2. 第一層順序並沒有按照順序寫,我相信大家不會沒事開物件的時候都是按照順序寫的,沒有這麼細節控
這時候我們直接 JSON.net 輸出成 JSON 會長這樣
var user1 = new User { Birth = DateTime.Now, Id = "USER1", Memo = "I am User1", Salary = 1_000_000 };
var user2 = new User { Birth = DateTime.Now, Id = "USER2", Memo = "I am User2", Salary = 2_000_000 };
var user3 = new User { Birth = DateTime.Now, Id = "USER3", Memo = "I am User3", Salary = 3_000_000 };
user1.Friends.Add(user2);
user1.Friends.Add(user3);
var json = JsonConvert.SerializeObject(user1);
Console.WriteLine(json);
//{"Id":"USER1","Birth":"2022-02-24T14:04:08.8661027+08:00",
//"Salary":1000000.0,"Memo":"I am User1",
//"Friends":[{"Id":"USER2","Birth":"2022-02-24T14:04:08.8680447+08:00","Salary":2000000.0,"Memo":"I am User2","Friends":[]},{"Id":"USER3","Birth":"2022-02-24T14:04:08.8680509+08:00","Salary":3000000.0,"Memo":"I am User3","Friends":[]}]}
這裡面並沒有按照順序,只會按照你寫的時候的順序。
3. 方法1 : 先轉成 JObject 後進行排序,這裡面有一行我註解掉了,原因是因為如果你需要針對第二層的話再打開
var test1 = SortPropertiesAlphabetically(JObject.FromObject(user1));
Console.WriteLine(JsonConvert.SerializeObject(test1));
//{"Birth":"2022-02-24T14:04:08.8661027+08:00",
//"Friends":[{"Id":"USER2","Birth":"2022-02-24T14:04:08.8680447+08:00","Salary":2000000.0,"Memo":"I am User2","Friends":[]},{"Id":"USER3","Birth":"2022-02-24T14:04:08.8680509+08:00","Salary":3000000.0,"Memo":"I am User3","Friends":[]}],
//"Id":"USER1","Memo":"I am User1",
//"Salary":1000000.0}
private static JObject SortPropertiesAlphabetically(JObject source)
{
var result = new JObject();
foreach (var property in source.Properties().ToList().OrderBy(p => p.Name))
{
var value = property.Value as JObject;
if (value != null)
{
//因為只需要第一層
//所以我將遞迴的地方拿掉
//value = SortPropertiesAlphabetically(value);
result.Add(property.Name, value);
}
else
{
result.Add(property.Name, property.Value);
}
}
return result;
}
4. 方法2: 先轉成 ExpandoObject 排序後再輸出
var expando = new ExpandoObject();
var dictionary = (IDictionary)expando;
foreach (var property in user1.GetType().GetProperties().OrderBy(x => x.Name))
{
dictionary.Add(property.Name, property.GetValue(user1));
}
Console.WriteLine("----");
Console.WriteLine(JsonConvert.SerializeObject(dictionary));
//{"Birth":"2022-02-24T14:04:08.8661027+08:00",
//"Friends":[{"Id":"USER2","Birth":"2022-02-24T14:04:08.8680447+08:00","Salary":2000000.0,"Memo":"I am User2","Friends":[]},{"Id":"USER3","Birth":"2022-02-24T14:04:08.8680509+08:00","Salary":3000000.0,"Memo":"I am User3","Friends":[]}],
//"Id":"USER1","Memo":"I am User1",
//"Salary":1000000.0}
這兩種方法都可以只是一個先轉成 JObject 還有一個就是 ExpandoObject 其實概念上不變使用的物件不同。
最近有要用到翻了以前的 code 記錄一下。
reference:
https://stackoverflow.com/questions/3330989/order-of-serialized-fields-using-json-net