Json.Net輸出合約實作:將所有的null字串轉為空字串

Newtonsoft的Json.Net迄今仍然是我序列化物件的首選,操作現代化的網站中絕對不可缺少的JSON格式的好朋友,今天要來深入討論Newtonsoft.Json.JsonSerializerSettings裡面的合約問題。

在有些狀態下,我們會希望前端的物件屬性中,不要傳入太多的出現null的資料,原因是因為你要特別為了這些null去多寫許多Javascript的檢查與轉換,在前端與後端都是我們自己在使用的情況下,的確有一點脫褲子放屁。

DefaultContractResolver來救贖我們了

Newtonsoft.Json.Serialization.DefaultContractResolver可以用來客製化許多我們期望的彈性,以下是程式碼的實作。

/// <summary>
/// 實作Newtonsoft.Json的DefaultContractResolver
/// </summary>
public class JsonStringNullToEmpty : Newtonsoft.Json.Serialization.DefaultContractResolver
{
  public JsonStringNullToEmpty() { }

  protected override System.Collections.Generic.IList<Newtonsoft.Json.Serialization.JsonProperty> CreateProperties(System.Type oType, Newtonsoft.Json.MemberSerialization oMS)
  {
    return oType.GetProperties().Select(oP =>
    {
      var oJP = base.CreateProperty(oP, oMS);
      oJP.ValueProvider = new JsonStringNullToEmptyValueProvider(oP);
      return oJP;
    }).ToList();
  }
}

/// <summary>
/// 實作Newtonsoft.Json的IValueProvider
/// </summary>
internal class JsonStringNullToEmptyValueProvider : Newtonsoft.Json.Serialization.IValueProvider
{
  System.Reflection.PropertyInfo _oMemberInfo;

  //建構子(將成員資訊帶入成為內部變數)
  public JsonStringNullToEmptyValueProvider(System.Reflection.PropertyInfo oMI) { _oMemberInfo = oMI; }

  //實作IValueProvider介面的寫入動作
  public void SetValue(object oTarget, object oValue) { _oMemberInfo.SetValue(oTarget, oValue); }

  //實作IValueProvider介面的寫入動作
  public object GetValue(object oTarget)
  {
    //設定回傳變數
    object oResult = _oMemberInfo.GetValue(oTarget);
    //若成員為字串型態,就處理他
    if (_oMemberInfo.PropertyType == typeof(System.String) && oResult == null) oResult = string.Empty;
    //若成員為表格型態,就進入巡訪
    if (_oMemberInfo.PropertyType == typeof(System.Data.DataTable))
    {
      System.Data.DataTable oDT = (System.Data.DataTable)oResult;
      foreach (System.Data.DataRow oDR in oDT.Rows)
      {
        foreach (System.Data.DataColumn oDC in oDT.Columns)
        {
          if (oDC.DataType == typeof(System.String))
          {
            oDR[oDC.ColumnName] = oDR[oDC.ColumnName] as string ?? string.Empty;
          }
        }
      }
      oResult = oDT;
    }
    //回傳結果
    return oResult;
  }
}

程式碼其實也沒甚麼好說明的,大致上就是開了一個覆寫方法,來把本來JSON.NET的CreateProperties基底方法蓋掉,然後在實作一個IValueProvider方法,在CreateProperties裡面將ValueProvider委派給JsonStringNullToEmptyValueProvider,裡面就是我們自己要處理的資料邏輯了。以上面這個程式碼來說,他只處理出現在型別為string或是出現在DataTable裡面的文字型別,其他的他就不處理了。

套用DefaultContractResolver合約

套用方式很簡單,請參考下列的程式碼,你懂得!

public static void Main()
{
  Employee oEmployee = new Employee();
  oEmployee.cNameFirst  = "John";
  oEmployee.cNameMiddle = "";
  //oEmployee.cNameLast = null; //這一行無論有沒有寫,他的輸出永遠都會是「""」
  
  Console.WriteLine(
    Newtonsoft.Json.JsonConvert.SerializeObject(
      oEmployee,
      Newtonsoft.Json.Formatting.None,
      new Newtonsoft.Json.JsonSerializerSettings { ContractResolver = new JsonStringNullToEmpty() }
    )
  );    
}

class Employee
{
  public string cNameFirst  { get; set; }
  public string cNameMiddle { get; set; }
  public string cNameLast   { get; set; }
}
Newtonsoft Json.Net DefaultContractResolver IValueProvider ORM POCO String Null Convert String.Empty