[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 class 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的成員屬性。

    [JsonObject(MemberSerialization.OptIn)]
public class Person
{


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



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

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

    public class ShouldSerializeContractResolver : DefaultContractResolver
{
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


  • Conditional Property Serialization


  • Reducing Serialized JSON Size