[C#]Json.NET - Reducing Serialized JSON Size

[C#]Json.NET - Reducing Serialized JSON Size

筆者在[C#]Json.NET - A high performance Json library這篇簡單的帶過了一下JSON.NET這個序列化函式庫,基本的操作只要理解那篇大概都不成問題,但最近在使用上JSON.NET又碰到一個問題,就是有時候沒有資料並不是總是不要序列化,所以我們不能帶上JsonIgnoreAttribute去總是忽略它,而是必須要做些特殊的判斷,像是欄位有時候會是Null或是空的集合,這時候我們序列化傳送也只是增加無謂的資料量,因此我們必須做些對此做些處理。

 

接下來筆者以[C#]Json.NET - A high performance Json library這篇的例子來做個簡單的說明,假設我們有個Person類別長得像下面這樣:

{
	public String Name { get; set; }

	public String NickName { get; set; }

	public DateTime Birthday { get; set; }

	public int Age 
	{
		get
		{
			return (int)((DateTime.Now - Birthday).TotalDays / 365);
		}
	}
}

 

裡面的NickName成員屬性就是個很好的例子,若NickName是忽略不序列化,那我們永遠無法從其它的地方推斷回來,它並不是跟Age這個成員屬性一樣可以透過推算所以可以直接忽略,但是若不做任何處理,當值為Null時它序列化出來的資訊也沒甚麼意義。

DateTime date = DateTime.Now;

Person Larry = new Person
{
	Name = "Larry Nung",
	Birthday = new DateTime(1980,4,19)
};

var json = JsonConvert.SerializeObject(Larry, Formatting.Indented);

Console.WriteLine(json);

 

image

 

這時可以在成員屬性上加上JsonPropertyAttribute,並指定NullValueHandling的處理,像是:

[JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
public String NickName { get; set; }
...

 

當成員屬性的值為Null時,序列化出來的結果就會被自動忽略掉。

image

 

套用這個屬性可以解決掉大多參考型態的成員屬性的問題,但是當成員屬性的型態為字串或是集合時就不完全適用,因為集合成員屬性可能不為Null但是是空的,而字串成員屬性可能不為Null但是是空字串。

Person Larry = new Person
{
    Name = "Larry Nung",
    NickName = "",
    Birthday = new DateTime(1980, 4, 19)
};
...

 

這樣的情況下序列出來的資料也是沒有什麼意義。

image

 

為了徹底解決這樣的問題,我們可以設定序列化的條件,像是為類別加上個名為ShowSerializeXXX的成員方法,用以決定什麼時候應該要序列化名為XXX的成員屬性。

    public class Person
    {
        ...

        [JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
        public String NickName { get; set; }

       ...

        public bool ShouldSerializeNickName()
        {
            return !string.IsNullOrEmpty(NickName);
        }
    }

 

又或者你不想要去更動到用來序列化的類別那邊,我們也可以加個ContractResolver類別,在CreateProperty方法裡面去決定要怎樣判斷是否要序列化,並將該類別的物件實體在序列化時帶入也可以。

    {
        public new static readonly ShouldSerializeContractResolver Instance = new ShouldSerializeContractResolver();

        protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
        {
            JsonProperty property = base.CreateProperty(member, memberSerialization);

            if (property.DeclaringType == typeof(Person) && property.PropertyName == "NickName")
            {
                property.ShouldSerialize =
                  instance =>
                  {
                      Person e = (Person)instance;
                      return !string.IsNullOrEmpty(e.NickName);
                  };
            }

            return property;
        }
    }
    ...
    var json = JsonConvert.SerializeObject(Larry, Formatting.Indented, new JsonSerializerSettings
    {
            ContractResolver = new ShouldSerializeContractResolver()
    });
 

 

運行起來就會發現這些沒意義的序列化資料都被濾掉了。

image

 

Link