• JSON.NET 使用技巧


    1. 序列化相关技巧

    通过特性忽略某些属性

    有时候我们会有这样的需求,我们只需要序列化实体类中的一部分属性,这时候我们可以通过声明忽略掉一些我们不需要序列化的属性,有两种方式可以使用么达到这个目标:

    首先,可以考虑使用JsonIgnore特性修饰不需要进行序列化的属性,如下所示:

    public class EmployeeBean
    {
        public Guid Id { get; set; }
        public string Name { get; set; }
        public string Email { get; set; }
        public decimal Salary { get; set; }
        public string Phone { get; set; }
        [JsonIgnore]
        public DateTime HireDate { get; set; }
    }
    

    运行程序:

    var employeeBean = new EmployeeBean()
    {
        Id = Guid.NewGuid(),
        Name = "gyzhao",
        Email = "gyzhao@gyzhao.com",
        Salary = 10000,
        Phone = "13912390987",
        HireDate = new DateTime(2012, 2, 1)
    };
        
    var jsonString = JsonConvert.SerializeObject(employeeBean, Formatting.Indented);
    //输出:
    //{
    //  "Id": "69a406ad-902c-45d3-8ba7-89a09779ed52",
    //  "Name": "gyzhao",
    //  "Email": "gyzhao@gyzhao.com",
    //  "Salary": 10000.0,
    //  "Phone": "13912390987"
    //}
    

    如果说你需要序列化的类有很多的属性,而你是需要使用其中的一小部分,如果使用上面的上面方式就会比较繁琐(因为需要忽略的属性太多了),这时候可以考虑使用DataContract特性修饰被序列化的类,使用DataMember特性修饰需要进行序列化的属性,其他没有该特性属性会被自动忽略掉。如下所示:

    [DataContract]
    public class EmployeeBean
    {
        [DataMember]
        public Guid Id { get; set; }
        [DataMember]
        public string Name { get; set; }
        [DataMember]
        public string Email { get; set; }
        [DataMember]
        public decimal Salary { get; set; }
        public string Phone { get; set; }
    
        public DateTime? HireDate { get; set; }
    }
    

    运行程序:

    var employeeBean = new EmployeeBean()
    {
        Id = Guid.NewGuid(),
        Name = "gyzhao",
        Email = "gyzhao@gyzhao.com",
        Salary = 10000,
        Phone = "13912390987",
        HireDate = new DateTime(2012, 2, 1)
    };
        
    var jsonString = JsonConvert.SerializeObject(employeeBean, Formatting.Indented);
    //输出:
    //{
    //  "Id": "69a406ad-902c-45d3-8ba7-89a09779ed52",
    //  "Name": "gyzhao",
    //  "Email": "gyzhao@gyzhao.com",
    //  "Salary": 10000.0
    //}
    

    DataContract特性和DataMember特性都从属于: System.Runtime.Serialization命名空间。

    动态序列化对象属性

    多谢园友 @夜色、花清浅 的提醒,确实有这样的场景:更多的我们可能需要的是动态的来确定需要序列化哪些属性,比如对于EmployeeBean来说:A方法需要序列化 NameId 属性,而 B方法需要序列化 EmailPhone 属性,在这种情况下,前面的两种使用特性的方式并不能很好的适应需求的变化,通过查询 JSON.NET 的文档(传送门:Json.NET Documentation),官方文档提供了这个API的示例程序,下面是改进的示例:

    var employeeBean = new EmployeeBean()
    {
        Id = Guid.NewGuid(),
        Name = "gyzhao",
        Email = "gyzhao@gyzhao.com",
        Salary = 10000,
        Phone = "13912390987",
        HireDate = new DateTime(2015, 5, 4)
    };
    
    var perperties = new List<string>()
    {
        employeeBean.GetPropertyName(t => t.Email),
        employeeBean.GetPropertyName(t => t.Phone)
    };
    
    var jsonString = JsonConvert.SerializeObject(employeeBean, Formatting.Indented, new JsonSerializerSettings()
    {
        ContractResolver = new JsonDynamicContractResolver(perperties)
        });
    
    //{
    //  "Email": "gyzhao@gyzhao.com",
    //  "Phone": "13912390987"
    //}
    Console.WriteLine(jsonString);
    

    下面是定义 JsonDynamicContractResolver 类的定义:

    public class JsonDynamicContractResolver : DefaultContractResolver
    {  
        private readonly List<string> _propertiesList; 
        public JsonDynamicContractResolver(IEnumerable<string> propertiesEnumerable)
        {
            if (propertiesEnumerable != null)
            {
                _propertiesList = propertiesEnumerable.ToList();
            }
        }
    
        protected override IList<JsonProperty> CreateProperties(Type type, MemberSerialization memberSerialization)
        {
            IList<JsonProperty> properties = base.CreateProperties(type, memberSerialization);
    
            //只序列化构造器中传入的包含在字符串中的属性
            if (_propertiesList != null && _propertiesList.Any())
            {
                properties =
                properties.Where(p => _propertiesList.Exists(pString => pString == p.PropertyName)).ToList();
            }
            return properties;
        }
    }
    

    在 传入 JsonDynamicContractResolver 构造函数中的指定序列化属性的集合时,我在这里使用了扩展方法:GetPropertyName ,这个方法通过传入一个 Lambda 表达式来获取需要序列化属性的字符串表示,这里是通过表达式树来实现的。相对于直接硬编码属性名称的字符串来说,使用表达式树动态获取在效率上有所损失(可接受的程度),不过换取的是设计上的灵活。比如:当我们更改属性名称时,编译器可以为我们提供类型安全的保护。而如果硬编码的话,如果一旦忘记修改,那么运行就会抛出异常,特别是系统中如果有很多地方都是用这种硬编码方式的话,那么维护起来就是一个噩梦了。下面是该扩展方法的代码:

    public static class Extensions
    {
    
        /// <summary>
        /// 获取对象实例属性的字符串表示
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="obj"></param>
        /// <param name="func"></param>
        /// <returns></returns>
        public static string GetPropertyName<T>(this T obj, Expression<Func<T, object>> func)
        {
            var propertyName = string.Empty;
            var expression = func.Body as UnaryExpression;
            if (expression != null)
            {
                propertyName = ((MemberExpression) expression.Operand).Member.Name;
            }
            else
            {
                var memberExpression = func.Body as MemberExpression;
                if (memberExpression != null)
                {
                    propertyName = memberExpression.Member.Name;
                }
                else
                {
                    var body = func.Body as ParameterExpression;
                    if (body != null)
                    {
                        propertyName = body.Type.Name;
                    }
                }
            }
            return propertyName;
        }
    }
    

    序列化对象时循环引用异常的解决办法

    序列化一个对象时,如果该对象有一个集合属性,改集合的类型就是对象本身的话,默认序列化的方法会报一个循环引用的异常,如果需要序列化,只需声明下面的属性即可:

    JsonConvert.SerializeObject(result,new JsonSerializerSettings{ReferenceLoopHandling=ReferenceLoopHandling.Serialize})
    

    2. 反序列化相关技巧

    2.1 使用匿名类型作为反序列化实体

    var jsonString = @"{
                        'Id': '69a406ad-902c-45d3-8ba7-89a09779ed52',
                        'Name': 'gyzhao',
                        'Salary': 10000.0,
                        'HireDate': '2012-02-01T00:00:00'
                       }";
    var employee = new
                    {
                        Name = default(string),
                        Salary = default(decimal),
                        HireDate = default(DateTime),
                        Id = default(Guid)
                    };
    var employeeBean = JsonConvert.DeserializeAnonymousType(jsonString, employee);
    

    3. 创建JSON

    //命令式的创建JSON对象
    var array = new JArray();
    var text = new JValue("Manual text");
    var date = new JValue(DateTime.Now);
    
    array.Add(text);
    array.Add(date);
    
    Console.WriteLine(array.ToString());
    
    //使用声明式的语法
    var rss =
        new JObject(
            new JProperty("channel", new JObject(
                new JProperty("title", "James Nexton-king"),
                new JProperty("link", "http://james.newtonking.com"),
                new JProperty("description", "James Newton-Kin's blog."),
                new JProperty("item", "BB"))));
    Console.WriteLine(rss.ToString());
    
    //通过一个匿名对象创建JSON
    JObject o = JObject.FromObject(new
    {
        channel = new
        {
            title = "James Newton-king",
            link = "http://james.netwoing.com",
            item = new List<string>()
            {
                "A",
                "B",
                "C",
                "D",
                "E"
            }
        }
    });
    Console.WriteLine(o.ToString());
    

    参考&进一步阅读

    http://www.newtonsoft.com/json

  • 相关阅读:
    揆首:以极客的思维做云诺
    [置顶] iOS学习笔记47——图片异步加载之EGOImageLoading
    wxWidgets初学者导引(3)——wxWidgets应用程序初体验(PDF版及附件下载)
    Win7 下用 VS2015 编译最新 openssl(1.0.2j)包含32、64位debug和release版本的dll、lib(8个版本)
    十问华为战略营销总裁徐文伟
    Debug与Release有时候确实不一致
    COM实践经验
    [置顶] (游戏编程-04)JAVA版雷电(奇迹冬瓜)
    第23章 COM和ActiveX(COM可以实现跨进程跨机器的函数调用)
    用Delphi即时判断当前的网络的连接方式
  • 原文地址:https://www.cnblogs.com/IPrograming/p/4472498.html
Copyright © 2020-2023  润新知