1. 序列化相關技巧
通過特性忽略某些屬性
有時候我們會有這樣的需求,我們只需要序列化實體類中的一部分屬性,這時候我們可以通過聲明忽略掉一些我們不需要序列化的屬性,有兩種方式可以使用么達到這個目標:
首先,可以考慮使用JsonIgnore
特性修飾不需要進行序列化的屬性,如下所示:
public class EmployeeBean
{
public Guid Id { get; set; }
public string Name { get; set; }
public string Email { get; set; }
public decimal Salary { get; set; }
public string Phone { get; set; }
[JsonIgnore]
public DateTime HireDate { get; set; }
}
運行程序:
var employeeBean = new EmployeeBean()
{
Id = Guid.NewGuid(),
Name = "gyzhao",
Email = "gyzhao@gyzhao.com",
Salary = 10000,
Phone = "13912390987",
HireDate = new DateTime(2012, 2, 1)
};
var jsonString = JsonConvert.SerializeObject(employeeBean, Formatting.Indented);
//輸出:
//{
// "Id": "69a406ad-902c-45d3-8ba7-89a09779ed52",
// "Name": "gyzhao",
// "Email": "gyzhao@gyzhao.com",
// "Salary": 10000.0,
// "Phone": "13912390987"
//}
如果說你需要序列化的類有很多的屬性,而你是需要使用其中的一小部分,如果使用上面的上面方式就會比較繁瑣(因為需要忽略的屬性太多了),這時候可以考慮使用DataContract
特性修飾被序列化的類,使用DataMember
特性修飾需要進行序列化的屬性,其他沒有該特性屬性會被自動忽略掉。如下所示:
[DataContract]
public class EmployeeBean
{
[DataMember]
public Guid Id { get; set; }
[DataMember]
public string Name { get; set; }
[DataMember]
public string Email { get; set; }
[DataMember]
public decimal Salary { get; set; }
public string Phone { get; set; }
public DateTime? HireDate { get; set; }
}
運行程序:
var employeeBean = new EmployeeBean()
{
Id = Guid.NewGuid(),
Name = "gyzhao",
Email = "gyzhao@gyzhao.com",
Salary = 10000,
Phone = "13912390987",
HireDate = new DateTime(2012, 2, 1)
};
var jsonString = JsonConvert.SerializeObject(employeeBean, Formatting.Indented);
//輸出:
//{
// "Id": "69a406ad-902c-45d3-8ba7-89a09779ed52",
// "Name": "gyzhao",
// "Email": "gyzhao@gyzhao.com",
// "Salary": 10000.0
//}
DataContract
特性和DataMember
特性都從屬於:System.Runtime.Serialization
命名空間。
動態序列化對象屬性
多謝園友 @夜色、花清淺 的提醒,確實有這樣的場景:更多的我們可能需要的是動態的來確定需要序列化哪些屬性,比如對於EmployeeBean來說:A方法需要序列化 Name
和 Id
屬性,而 B方法需要序列化 Email
和 Phone
屬性,在這種情況下,前面的兩種使用特性的方式並不能很好的適應需求的變化,通過查詢 JSON.NET 的文檔(傳送門:Json.NET Documentation),官方文檔提供了這個API的示例程序,下面是改進的示例:
var employeeBean = new EmployeeBean()
{
Id = Guid.NewGuid(),
Name = "gyzhao",
Email = "gyzhao@gyzhao.com",
Salary = 10000,
Phone = "13912390987",
HireDate = new DateTime(2015, 5, 4)
};
var perperties = new List<string>()
{
employeeBean.GetPropertyName(t => t.Email),
employeeBean.GetPropertyName(t => t.Phone)
};
var jsonString = JsonConvert.SerializeObject(employeeBean, Formatting.Indented, new JsonSerializerSettings()
{
ContractResolver = new JsonDynamicContractResolver(perperties)
});
//{
// "Email": "gyzhao@gyzhao.com",
// "Phone": "13912390987"
//}
Console.WriteLine(jsonString);
下面是定義 JsonDynamicContractResolver
類的定義:
public class JsonDynamicContractResolver : DefaultContractResolver
{
private readonly List<string> _propertiesList;
public JsonDynamicContractResolver(IEnumerable<string> propertiesEnumerable)
{
if (propertiesEnumerable != null)
{
_propertiesList = propertiesEnumerable.ToList();
}
}
protected override IList<JsonProperty> CreateProperties(Type type, MemberSerialization memberSerialization)
{
IList<JsonProperty> properties = base.CreateProperties(type, memberSerialization);
//只序列化構造器中傳入的包含在字符串中的屬性
if (_propertiesList != null && _propertiesList.Any())
{
properties =
properties.Where(p => _propertiesList.Exists(pString => pString == p.PropertyName)).ToList();
}
return properties;
}
}
在 傳入 JsonDynamicContractResolver
構造函數中的指定序列化屬性的集合時,我在這里使用了擴展方法:GetPropertyName
,這個方法通過傳入一個 Lambda
表達式來獲取需要序列化屬性的字符串表示,這里是通過表達式樹來實現的。相對於直接硬編碼屬性名稱的字符串來說,使用表達式樹動態獲取在效率上有所損失(可接受的程度),不過換取的是設計上的靈活。比如:當我們更改屬性名稱時,編譯器可以為我們提供類型安全的保護。而如果硬編碼的話,如果一旦忘記修改,那么運行就會拋出異常,特別是系統中如果有很多地方都是用這種硬編碼方式的話,那么維護起來就是一個噩夢了。下面是該擴展方法的代碼:
public static class Extensions
{
/// <summary>
/// 獲取對象實例屬性的字符串表示
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="obj"></param>
/// <param name="func"></param>
/// <returns></returns>
public static string GetPropertyName<T>(this T obj, Expression<Func<T, object>> func)
{
var propertyName = string.Empty;
var expression = func.Body as UnaryExpression;
if (expression != null)
{
propertyName = ((MemberExpression) expression.Operand).Member.Name;
}
else
{
var memberExpression = func.Body as MemberExpression;
if (memberExpression != null)
{
propertyName = memberExpression.Member.Name;
}
else
{
var body = func.Body as ParameterExpression;
if (body != null)
{
propertyName = body.Type.Name;
}
}
}
return propertyName;
}
}
序列化對象時循環引用異常的解決辦法
序列化一個對象時,如果該對象有一個集合屬性,改集合的類型就是對象本身的話,默認序列化的方法會報一個循環引用的異常,如果需要序列化,只需聲明下面的屬性即可:
JsonConvert.SerializeObject(result,new JsonSerializerSettings{ReferenceLoopHandling=ReferenceLoopHandling.Serialize})
2. 反序列化相關技巧
2.1 使用匿名類型作為反序列化實體
var jsonString = @"{
'Id': '69a406ad-902c-45d3-8ba7-89a09779ed52',
'Name': 'gyzhao',
'Salary': 10000.0,
'HireDate': '2012-02-01T00:00:00'
}";
var employee = new
{
Name = default(string),
Salary = default(decimal),
HireDate = default(DateTime),
Id = default(Guid)
};
var employeeBean = JsonConvert.DeserializeAnonymousType(jsonString, employee);
3. 創建JSON
//命令式的創建JSON對象
var array = new JArray();
var text = new JValue("Manual text");
var date = new JValue(DateTime.Now);
array.Add(text);
array.Add(date);
Console.WriteLine(array.ToString());
//使用聲明式的語法
var rss =
new JObject(
new JProperty("channel", new JObject(
new JProperty("title", "James Nexton-king"),
new JProperty("link", "http://james.newtonking.com"),
new JProperty("description", "James Newton-Kin's blog."),
new JProperty("item", "BB"))));
Console.WriteLine(rss.ToString());
//通過一個匿名對象創建JSON
JObject o = JObject.FromObject(new
{
channel = new
{
title = "James Newton-king",
link = "http://james.netwoing.com",
item = new List<string>()
{
"A",
"B",
"C",
"D",
"E"
}
}
});
Console.WriteLine(o.ToString());