再谈Newtonsoft.Json高级用法

  上一篇Newtonsoft.Json高级用法发布以后收到挺多回复的,本篇将分享几点挺有用的知识点和最近项目中用到的一个新点进行说明,做为对上篇文章的补充。

阅读目录

  • 动态改变属性序列化名称
  • 枚举值序列化问题
  • 全局设置
  • 总结

回到顶部

动态改变属性序列化名称

  "动态改变属性序列化名称"顾名思义:在不同场景下实体字段序列化后字段名称不同,比如有下面实体A,正常序列化后json为{"Id":"123"}

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

现在有两种新场景A场景下 字段Id需要序列化为Key,B场景下字段Id需要序列化为id,那么如何在不改变实体代码情形下完成该功能呢?下面以树形结构数据为例子进行讲解。

各种各样的前端树形控件所要求数据格式不一样,下面列举几种常见的树形控件数据格式。

//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处理

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);
            }
        }
    }

调用代码实例

  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,后续有好的问题会在本篇文章进行续写。

如果,您认为阅读这篇博客让您有些收获,不妨点击一下右下角的【推荐】按钮。
如果,您希望更容易地发现我的新博客,不妨点击一下绿色通道的【关注我】。
因为,我的写作热情也离不开您的肯定支持。

感谢您的阅读,如果您对我的博客所讲述的内容有兴趣,请继续关注我的后续博客,我是焰尾迭 。

时间: 2024-12-15 11:11:18

再谈Newtonsoft.Json高级用法的相关文章

Newtonsoft.Json高级用法之枚举中文转义

最近看博客园中 焰尾迭的两篇关于"Newtonsoft.Json高级用法"的文章受到了很多人的评论,一度登入到头条推荐. 今天我就不再重复焰尾迭博文中的一些提过的Newtonsoft.Json的高级用法.大家如果想知道直接去看. Newtonsoft.Json高级用法 再谈Newtonsoft.Json高级用法 我主要说焰尾迭没有提到的用法——枚举中文转义 枚举值序列化问题(摘自焰尾迭段落) public enum NotifyType { /// <summary> //

Newtonsoft.Json高级用法

手机端应用讲究速度快,体验好.刚好手头上的一个项目服务端接口有性能问题,需要进行优化.在接口多次修改中,实体添加了很多字段用于中间计算或者存储,然后最终用Newtonsoft.Json进行序列化返回数据,经过分析一个简单的列表接口每一行数据返回了16个字段,但是手机APP端只用到了其中7个字段,剩余9个字段的数据全部都是多余的,如果接口返回数据为40K大小,也就是说大约20K的数据为无效数据,3G网络下20K下载差不多需要1s,不返回无效数据至少可以节约1s的时间,大大提高用户体验.本篇将为大家

Newtonsoft.Json 高级用法

基本用法 Json.NET是支持序列化和反序列化DataTable,DataSet,Entity Framework和Entity的.下面分别举例说明序列化和反序列化. DataTable: //序列化DataTable DataTable dt = new DataTable(); dt.Columns.Add("Age", Type.GetType("System.Int32")); dt.Columns.Add("Name", Type.G

Newtonsoft.Json高级用法--转载至 焰尾迭 随笔

本人只做搬运工,以这方便自己学习的态度!以下内容均为拷贝! 如有不适请联系本人! 本文原作者:焰尾迭 本文地址:http://www.cnblogs.com/yanweidie/p/4605212.html#commentform 手机端应用讲究速度快,体验好.刚好手头上的一个项目服务端接口有性能问题,需要进行优化.在接口多次修改中,实体添加了很多字段用于中间计算或者存储,然后最终用Newtonsoft.Json进行序列化返回数据,经过分析一个简单的列表接口每一行数据返回了16个字段,但是手机A

【Json】Newtonsoft.Json高级用法

手机端应用讲究速度快,体验好.刚好手头上的一个项目服务端接口有性能问题,需要进行优化.在接口多次修改中,实体添加了很多字段用于中间计算或者存储,然后最终用Newtonsoft.Json进行序列化返回数据,经过分析一个简单的列表接口每一行数据返回了16个字段,但是手机APP端只用到了其中7个字段,剩余9个字段的数据全部都是多余的,如果接口返回数据为40K大小,也就是说大约20K的数据为无效数据,3G网络下20K下载差不多需要1s,不返回无效数据至少可以节约1s的时间,大大提高用户体验.本篇将为大家

转:Newtonsoft.Json高级用法

原文地址:http://www.cnblogs.com/yanweidie/p/4605212.html 手机端应用讲究速度快,体验好.刚好手头上的一个项目服务端接口有性能问题,需要进行优化.在接口多次修改中,实体添加了很多字段用于中间计算或者存储,然后最终用Newtonsoft.Json进行序列化返回数据,经过分析一个简单的列表接口每一行数据返回了16个字段,但是手机APP端只用到了其中7个字段,剩余9个字段的数据全部都是多余的,如果接口返回数据为40K大小,也就是说大约20K的数据为无效数据

Newtonsoft.Json高级用法 1.忽略某些属性 2.默认值的处理 3.空值的处理 4.支持非公共成员 5.日期处理 6.自定义序列化的字段名称

手机端应用讲究速度快,体验好.刚好手头上的一个项目服务端接口有性能问题,需要进行优化.在接口多次修改中,实体添加了很多字段用于中间计算或者存储,然后最终用Newtonsoft.Json进行序列化返回数据,经过分析一个简单的列表接口每一行数据返回了16个字段,但是手机APP端只用到了其中7个字段,剩余9个字段的数据全部都是多余的,如果接口返回数据为40K大小,也就是说大约20K的数据为无效数据,3G网络下20K下载差不多需要1s,不返回无效数据至少可以节约1s的时间,大大提高用户体验.本篇将为大家

【转】 Newtonsoft.Json高级用法

手机端应用讲究速度快,体验好.刚好手头上的一个项目服务端接口有性能问题,需要进行优化.在接口多次修改中,实体添加了很多字段用于中间计算或者存储,然后最终用Newtonsoft.Json进行序列化返回数据,经过分析一个简单的列表接口每一行数据返回了16个字段,但是手机APP端只用到了其中7个字段,剩余9个字段的数据全部都是多余的,如果接口返回数据为40K大小,也就是说大约20K的数据为无效数据,3G网络下20K下载差不多需要1s,不返回无效数据至少可以节约1s的时间,大大提高用户体验.本篇将为大家

Newtonsoft.Json高级用法(转载)

http://www.cnblogs.com/yanweidie/p/4605212.html#!comments 数据字典生成工具之旅系列文章导航