再談Newtonsoft.Json高級用法

  上一篇Newtonsoft.Json高級用法發佈之後收到挺多回復的,本篇將分享幾點挺有用的知識點和最近項目中用到的一個新點進行說明,作爲對上篇文章的補充。html

閱讀目錄前端

動態改變屬性序列化名稱

  "動態改變屬性序列化名稱"顧名思義:在不一樣場景下實體字段序列化後字段名稱不一樣,好比有下面實體A,正常序列化後json爲{"Id":"123"}node

    public class A
    {
        public string Id { get; set; }
    }

 

       如今有兩種新場景A場景下 字段Id須要序列化爲Key,B場景下字段Id須要序列化爲id,那麼如何在不改變實體代碼情形下完成該功能呢?下面以樹形結構數據爲例子進行講解。json

       各類各樣的前端樹形控件所要求數據格式不同,下面列舉幾種常見的樹形控件數據格式。bootstrap

//bootstrap treeview,數據結構爲
[
    {
            id:'1', //節點id
            text: '父節點',  //節點顯示文本
            icon: 'glyphicon glyphicon-cloud-download',  //節點圖標樣式
            nodes:[{id:'2',text:'子節點'}]  //子節點
    }
]

//zTree
[  
    { "id" : "1", "name" : "父節點1", "children" : [{id:'4',name:'子節點1'}] },  
    { "id" : "2", "name" : "父節點2", "children" : [{id:'5',name:'子節點2'}] },  
    { "id" : "3", "name" : "父節點3", "children" : [{id:'6',name:'子節點3'}] }  
]

  二者之間字段對比後端

  treeview zTree
節點id id id
顯示文本 text name
圖標 icon icon
子節點 nodes children

 標紅部分是數據格式區別,假設後臺定義的樹形實體以下數組

    /// <summary>
    /// 樹形實體
    /// </summary>
    public class Tree
    {
        /// <summary>
        /// 當前ID
        /// </summary>
        public string Id { get; set; }

        /// <summary>
        /// 文本
        /// </summary>
        public string Text { get; set; }

        /// <summary>
        /// 附加信息
        /// </summary>
        public string Tag { get; set; }

        /// <summary>
        /// 節點圖標
        /// </summary>
        public string Icon { get; set; }

        /// <summary>
        /// 子級
        /// </summary>
        public List<Tree> Childrens { get; set; }
    }
  如今的情形是這樣的,後臺樹形實體已經定義完成,前臺樹形控件使用的是treeview。有什麼辦法使後臺序列化返回的json數據格式和控件所要求的保持一致呢。
方法一 修改實體Tree 
    /// <summary>
    /// 樹形實體
    /// </summary>
    public class Tree
    {
        /// <summary>
        /// 當前ID
        /// </summary>
        public string id { get; set; }

        /// <summary>
        /// 文本
        /// </summary>
        public string text { get; set; }

        /// <summary>
        /// 附加信息
        /// </summary>
        public string Tag { get; set; }

        /// <summary>
        /// 節點圖標
        /// </summary>
        public string Icon { get; set; }

        /// <summary>
        /// 子級
        /// </summary>
        public List<Tree> nodes { get; set; }
    }

 其中標紅部分是修改的,固然還須要修改對Tree實體賦值的代碼,這裏就不列出了。數據結構

方法二 前臺js處理mvc

var data=[
            {"Id":"1","Text":"父節點1","Childrens":[
                {"Id":"3","Text":"子節點1","Childrens":[{"Id":"5","Text":"子節點1-1"}]},
                {"Id":"4","Text":"子節點2"}
            ]},
            {"Id":"2","Text":"父節點2","Childrens":[
                {"Id":"5","Text":"子節點3"}
            ]}]
        //將後臺返回數據轉換成treeview所需格式數據
        handleChild(data);
        console.log(data);
        
        //轉換後臺實體數據爲treeview符合的數據格式
        function handleChild(childs){
            for(var i=0,length=childs.length;i<length;i++){
                var item=childs[i];
                item.id=item.Id;
                item.text=item.Text;
                item.nodes=item.Childrens;
                //處理子節點
                if(item.Childrens){
                    handleChild(item.Childrens);
                }
                delete item.Id;
                delete item.Text;
                delete item.Childrens;
            }
        }
    以上兩種方法均可以很輕鬆的解決我上述提出的問題,項目進行到一半,treeview使用的也很好,一切都很太平。某一天遇到了一個難題,前臺有個功能須要使用zTree實現。可是須要保證以前使用treeView的功能模塊不變,又得支持zTree數據格式,先來分析一下上面兩種方案看還能不能繼續使用,方案一,能夠新建一個樹形實體專門和zTree對應。方案二,從新實現一套數據轉換代碼。以上兩種方案缺點很明顯,先後端依賴太強,前臺換了控件致使變更過大。
    在思考有沒有更好的解決方案時,我想到了高級序列化用法中 自定義序列化的字段名稱這一條,既然Newtonsoft.Json提供了實體字段A序列化成B的特性,那麼如今惟一須要解決的問題:怎麼動態修改這個映射關係。通過一番嘗試和閱讀源代碼,終於找到了下面最佳實踐。
 /// <summary>
    /// 動態屬性轉換約定
    /// </summary>
    /// <remarks>
    /// 處理場景 樹形結構數據 後臺代碼實體定義 爲 Id Childrens 可是前臺樹形控件所需數據結構爲  id,nodes
    /// 這個時候可使用該屬性約定轉換類 動態設置 序列化後字段名稱
    /// </remarks>
    /// <example>
    ///     JsonSerializerSettings setting = new JsonSerializerSettings();
    ///     setting.ContractResolver = new PropsContractResolver(new Dictionary<string, string> { { "Id", "id" }, { "Text", "text" }, { "Childrens", "nodes" } });
    ///     string AA = JsonConvert.SerializeObject(cc, Formatting.Indented, setting);
    /// </example>
    public class PropsContractResolver : DefaultContractResolver
    {
        Dictionary<string, string> dict_props = null;

        /// <summary>
        /// 構造函數
        /// </summary>
        /// <param name="props">傳入的屬性數組</param>
        public PropsContractResolver(Dictionary<string, string> dictPropertyName)
        {
            //指定字段要序列化成什麼名稱
            this.dict_props = dictPropertyName;
        }

        protected override string ResolvePropertyName(string propertyName)
        {
            string newPropertyName = string.Empty;
            if (dict_props != null && dict_props.TryGetValue(propertyName, out newPropertyName))
            {
                return newPropertyName;
            }
            else
            {
                return base.ResolvePropertyName(propertyName);
            }
        }
    }

 調用代碼實例ide

  string type="zTree";
  //字段映射關係
  Dictionary<string, string> _dictProp = null;
  if(type=="zTree"){
    _dictProp = new Dictionary<string, string> { { "Icon", "icon" }, { "Text", "name" }, { "Childrens", "children" } };
  }else if(type=="treeview"){
    _dictProp = new Dictionary<string, string> { { "Icon", "icon" }, { "Text", "text" }, { "Childrens", "nodes" } };
  }
                 
  // 序列化設置
  JsonSerializerSettings PropSettings = new JsonSerializerSettings { 
    ContractResolver = new PropsContractResolver(_dictProp)
  };
  return JsonConvert.SerializeObject(new List<Tree>(), Formatting.None, PropSettings);
 使用了 動態改變屬性序列化名稱方案後,先後臺徹底解綁了,無論前臺使用什麼樹形控件,後臺實體只有一個樹形實體。咱們要作的僅僅是設置一下字段映射關係而已。
 

枚舉值序列化問題

   默認狀況下對於實體裏面的枚舉類型系統是格式化成改枚舉對應的整型數值,那若是須要格式化成枚舉對應的字符怎麼處理呢?Newtonsoft.Json也幫咱們想到了這點,下面看實例

public enum NotifyType
    {
        /// <summary>
        /// Emil發送
        /// </summary>
        Mail=0,

        /// <summary>
        /// 短信發送
        /// </summary>
        SMS=1
    }

    public class TestEnmu
    {
        /// <summary>
        /// 消息發送類型
        /// </summary>
        public NotifyType Type { get; set; }
    }
    JsonConvert.SerializeObject(new TestEnmu());

 

輸出結果:  如今改造一下,輸出"Type":"Mail"

    public class TestEnmu
    {
        /// <summary>
        /// 消息發送類型
        /// </summary>
        [JsonConverter(typeof(StringEnumConverter))]
        public NotifyType Type { get; set; }
    }

 

其它的都不變,在Type屬性上加上了JsonConverter(typeof(StringEnumConverter))表示將枚舉值轉換成對應的字符串,而StringEnumConverter是Newtonsoft.Json內置的轉換類型,最終輸出結果

全局設置

  全局參數設置功能是我最喜歡使用的功能,如今作的mvc項目,我都會先設定空值處理,減小沒必要要的流量損耗。上篇文章開篇說了,最初研究Newtonsoft.Json是從移動端項目開始的,無用字段空值字段不返回。

Newtonsoft.Json.JsonSerializerSettings setting = new Newtonsoft.Json.JsonSerializerSettings();
   JsonConvert.DefaultSettings = new Func<JsonSerializerSettings>(() =>
   {
    //日期類型默認格式化處理
     setting.DateFormatHandling = Newtonsoft.Json.DateFormatHandling.MicrosoftDateFormat;
      setting.DateFormatString = "yyyy-MM-dd HH:mm:ss";

    //空值處理
      setting.NullValueHandling = NullValueHandling.Ignore;return setting;
   });

 

    

總結

  另外有關自定義類型轉換問題能夠參考Newtonsoft.Json高級用法第九條。序列化庫深刻使用以後,由衷的佩服做者,能夠將一個序列化庫作的如此強大,在學習它源代碼的同時對本身代碼設計理念也產生了很大的影響。感謝Newtonsoft.Json,後續有好的問題會在本篇文章進行續寫。

 

 

相關文章
相關標籤/搜索