原文 Json.Net系列教程 3.Json.Net序列化和反序列化設定

上節補充

首先補充一點,Json.Net是支援序列化和反序列化DataTable,DataSet,Entity Framework和NHibernate的.我舉例說明DataTable的序列化和反序列化.
建立一個DataTable物件,如下:

            DataTable dt = new DataTable();
DataColumn dcName = new DataColumn("Name");
DataColumn dcAge = new DataColumn("Age");
DataColumn dcCity = new DataColumn("City"); dt.Columns.Add(dcName);
dt.Columns.Add(dcAge);
dt.Columns.Add(dcCity);
for (int i = ; i < ; i++)
{
DataRow dr = dt.NewRow();
dr[] = "Name" + i;
dr[] = "Age" + i;
dr[] = "City" + i;
dt.Rows.Add(dr);
}

序列化:

            string json = JsonConvert.SerializeObject(dt);
Console.WriteLine(json);

結果:

利用上面得到的序列化字串反序列化:

            DataTable dt1 = JsonConvert.DeserializeObject<DataTable>(json);
for (int i = ; i < dt1.Rows.Count; i++)
{
DataRow dr = dt1.Rows[i];
Console.WriteLine("{0}\t{1}\t{2}\t", dr[], dr[], dr[]);
}

結果:

本節內容

預備知識:

要想實現對Json.Net序列化和反序列化的控制,就要用到類JsonSerializerSettings,用法很簡單例項化一個物件,並把它賦值給JsonConvert的引數即可.

            var jSetting = new JsonSerializerSettings();
string json = JsonConvert.SerializeObject(obj,jSetting);

開始之前,我還是先建立一員工類:

    public class Staff
{
public string Name { get; set; }
public int Age { get; set; }
public string Gender { get; set; }
public string DepartmentName { get; set; }
public Staff Leader { get; set; }
}
一.空值的處理

這裡的空值指的是引用型別為NULL時,Json.Net如何處理.通過設定jSetting.NullValueHandling的值來確定,該值為列舉型別.

NullValueHandling.Ignore
忽略為NULL的值
NullValueHandling.Include
預設值,包括為NULL的值

例項:

            Staff jack = new Staff { Name = "Jack", Age = , Gender = "Male", DepartmentName = "Personnel Department", Leader = null };
var jSetting = new JsonSerializerSettings();
jSetting.NullValueHandling = NullValueHandling.Ignore;
string json = JsonConvert.SerializeObject(jack,jSetting);
Console.WriteLine(json);

結果:

2.預設值的處理

一般是對於值型別的處理,通過設定jSetting.DefaultValueHandling的值來確定,該值為列舉型別.

DefaultValueHandling.Ignore
序列化和反序列化時,忽略預設值
DefaultValueHandling.Include
序列化和反序列化時,包含預設值

給成員設定默任值,用到"DefaultValue(value)"特性,當然別忘了引入名稱空間"System.ComponentModel",假設員工的年齡預設值為30

        [DefaultValue()]
public int Age { get; set; }

序列化時我想忽略為預設值的成員

            Staff jack = new Staff { Name = "Jack", Age = , Gender = "Male", DepartmentName = "Personnel Department", Leader = null };
var jSetting = new JsonSerializerSettings();
jSetting.DefaultValueHandling = DefaultValueHandling.Ignore;
string json = JsonConvert.SerializeObject(jack,jSetting);
Console.WriteLine(json);

結果:

3.忽略某些屬性

首先介紹Json.Net序列化的模式:OptOut 和 OptIn.

OptOut 預設值,類中所有公有成員會被序列化,如果不想被序列化,可以用特性JsonIgnore
OptIn 預設情況下,所有的成員不會被序列化,類中的成員只有標有特性JsonProperty的才會被序列化,當類的成員很多,但客戶端僅僅需要一部分資料時,很有用

假如客戶僅僅需要員工的姓名,此時

    [JsonObject(Newtonsoft.Json.MemberSerialization.OptIn)]
public class Staff
{
[JsonProperty]
public string Name { get; set; }
public int Age { get; set; }
public string Gender { get; set; }
public string DepartmentName { get; set; }
public Staff Leader { get; set; }
}

序列化:

            Staff jack = new Staff { Name = "Jack", Age = , Gender = "Male", DepartmentName = "Personnel Department", Leader = null };
string json = JsonConvert.SerializeObject(jack);

結果:

如果客戶不想要員工的領導資訊

    public class Staff
{
public string Name { get; set; }
public int Age { get; set; }
public string Gender { get; set; }
public string DepartmentName { get; set; }
[JsonIgnore]
public Staff Leader { get; set; }
}

序列化:

            Staff tom = new Staff { Name = "Tome", Age = , Gender = "Male", DepartmentName = "Personnel Department"};
Staff jack = new Staff { Name = "Jack", Age = , Gender = "Male", DepartmentName = "Personnel Department", Leader = tom };
string json = JsonConvert.SerializeObject(jack);
Console.WriteLine(json);

結果:

4.支援非公共成員

Json.Net序列化物件時,預設情況下僅僅序列化公有成員,如果想要非公有成員也被序列化,就要在該成員上加特性"JsonProperty"

5.日期處理

  JsonConverters會在序列化和反序列化時被用到。JsonConverters允許手動對Json的控制。當Json的結構很複雜 和你想改變一個型別怎麼樣被序列化時,這是非常有用的。當一個JsonConverters被新增到JsonSerializer時,它會檢查每一要被序 列化和反序列化的值,並返回CanConvert,如果為True,則JsonConverter讀和寫這個值;需要注意的是,雖然 JsonConverter能夠使你可以完全的控制Json的值,但是很多的Json.Net序列化的特性被限制,像是型別名稱和引用處理。所有的 JsonConvert都在名稱空間 "Newtonsoft.Json.Converters"下

5.1IsoDateTimeConverter 和 JavaScriptDateTimeConverter

這是Json.Net中自帶的兩個處理日期的類,預設是IsoDateTimeConverter ,它的格式是"yyyy'-'MM'-'dd'T'HH':'mm':'ss.FFFFFFFK".另一個是JavaScriptTimeConverter,它的格式是 "new Date(ticks)",其實返回的是一個JavaScript的Date物件.
有兩種方式來應用JsonConverter,改變Json序列化和反序列化的行為.

5.1.1如果你要序列化的日期格式是統一的,可以考慮如下方式

假設我們為員工新增兩個日期型別的成員,出生日期和入職日期

    public class Staff
{
public string Name { get; set; }
public int Age { get; set; }
public string Gender { get; set; }
public string DepartmentName { get; set; }
public Staff Leader { get; set; }
public DateTime BirthDate { get; set; }
public DateTime EmploymentDate { get; set; }
}

我們的客戶要求日期型別的成員返回javascript的日期物件

            Staff jack = new Staff { Name = "Jack", Age = , Gender = "Male",
DepartmentName = "Personnel Department", BirthDate = new DateTime(,,), EmploymentDate = new DateTime(,,) };
string json = JsonConvert.SerializeObject(jack,new JavaScriptDateTimeConverter());
Console.WriteLine(json);

結果:

5.1.2如果想要不同的日期型別成員序列化後,以不同的形式顯示.

現在我們的客戶要求出生日期以"ISO"標準日期格式返回,入職日期以Javascript的Date物件格式返回,修改我們的員工類

    public class Staff
{
public string Name { get; set; }
public int Age { get; set; }
public string Gender { get; set; }
public string DepartmentName { get; set; }
public Staff Leader { get; set; }
[JsonConverter(typeof(IsoDateTimeConverter))]
public DateTime BirthDate { get; set; }
[JsonConverter(typeof(JavaScriptDateTimeConverter))]
public DateTime EmploymentDate { get; set; }
}

是的,通過特性"JsonConverter"來實現差異化的
序列化:

            Staff jack = new Staff { Name = "Jack", Age = , Gender = "Male",
DepartmentName = "Personnel Department", BirthDate = new DateTime(,,), EmploymentDate = new DateTime(,,) };
string json = JsonConvert.SerializeObject(jack);
Console.WriteLine(json);

結果:

5.2自定義日期格式

客戶現在提出要求,希望得到的日期格式是符合中國人習慣的格式.要求返回的格式是"2012年4月20日".挑戰來了,沒有挑戰就沒有進步,我喜歡挑戰.光說是沒有用的!先分析一下怎麼解決這個問題.我考慮了兩種思路.
  思路一:
研究了一下上面兩個日期處理類,發現他們都是繼承了基類"DateTimeConverterBase",所以我們可以參考"IsoDatetimeConverter"的實現方式,自己新建一個處理日期格式的轉換器類.這種方式的缺點是可能要花大量的時間去研究,比較費時費力.優點就是可以對日期格式隨心所欲的控制.
  思路二:
我又研究了一下"IsoDatetimeConverter",發現它的日期格式其實是由於內部DefaultDateTimeFormat = "yyyy'-'MM'-'dd'T'HH':'mm':'ss.FFFFFFFK"導致,而它也提供了修改日期樣式的屬性"DateTimeFormat",只要我們按照這種格式來寫就OK了.

            Staff jack = new Staff { Name = "Jack", Age = , Gender = "Male",
DepartmentName = "Personnel Department", BirthDate = new DateTime(,,), EmploymentDate = new DateTime(,,) };
IsoDateTimeConverter dtConverter = new IsoDateTimeConverter { DateTimeFormat = "yyyy'年'MM'月'dd'日'" };
string json = JsonConvert.SerializeObject(jack,dtConverter);
Console.WriteLine(json);

結果:

6.FAQ
1.如何自定義序列化的欄位名稱?
預設情況下,Json.Net序列化後結果中的欄位名稱和類中屬性的名稱一致.如果想自定義序列化後的欄位名稱,可以使用JsonProperty.例如:
 public class Person
{
public int Id { get; set; } public string Name { get; set; }
}

預設序列化的結果為: {"Id":1,"Name":"楊過"},如果不想用預設的欄位名稱,可以使用如下方式:

public class Person
{
[JsonProperty(PropertyName = "PersonId")]
public int Id { get; set; } [JsonProperty(PropertyName = "PersonName")]
public string Name { get; set; }
}

這樣序列化的結果為:{"PersonId":1,"PersonName":"楊過"}