zoukankan      html  css  js  c++  java
  • Newtonsoft.Json动态过滤属性

    Newtonsoft.Json动态过滤属性

    接口写的多了,会发现很多的问题。同一个dto,不同的action返回的字段个数不一样。往往开发人员因为懒或者各种原因一股脑的全返回,会浪费很多流量且用户体验很差。

    当然也会有负责一些的,根据不同的action定义不同的output类。毫无疑问这很麻烦,浪费开发时间。博主本人也是深受其扰,之前看到一篇博文 Newtonsoft.Json高级用法 里面有说到动态决定是属性是否序列化。深得我心于是上手试了一下。博文里的代码很不完善

    本人的项目返回类型均为 OutputModel(Status、Message、Data(数据))。把output丢给json序列化的时候,拿到的只有属性只有Status、Message、Data。而我们要针对的是Data类型的属性。本人进行改造后。由于过滤的属性清单与OutPutModel的又不一致(因为我们要序列化的是output,我们传入的是data里的属性),无法进行序列化,后面又实验了一下效率如何。如下图,5w次循环竟然与之前差了71倍之多。于是博主准备自己动手去写一个

    FNM(GIU6AAL}3GGP2VOBIHG

    自己动手

    动手之前应该思考一下,我们需要的是什么效果。

    • 根据传入数组动态决定哪些属性需要初始化
    • 针对OutPutModel的处理
    • 效率的高效,至少要直接序列化的差距不会太大

    我想要用法是在序列化的时候传入一个字符串数组(也就是需要序列化属性),这里对思路进行梳理一下

    return Json(output,new []{"Name","Age"})
    • 使用反射,拿到output.Data的Type
    • 使用type创建一个实例,循环type的GetProperties。判断property是否在传入的字符串数组中
    • 使用property.GetValue获取到属性值,然后再SetValue到创建的实例中
    • 把实例赋值给output.Data

    很简单,就是对output的data进行一次替换,下面代码很完美的可以解决问题。

    private static void Filtered(OutputModel output, string[] param)
     {
            Type type = null;
            if (param == null || param.Length <= 0)
            {
                  return;
            }
            type = null;
       
             
            if (output.Data == null) return;
            type = output.Data.GetType();
            object result = Activator.CreateInstance(type);
            foreach (var property in type.GetProperties())
           {
               if (!((IList)param).Contains(property.Name)) continue;
               object value = property.GetValue(output.Data, null);
              property.SetValue(result, value);
           }
            output.Data = result;
       }

    细心的同学可能会发现上面的少了针对List的处理

          Type type = null;
          if (output.Data is IList)
                {
                    var dataList = output.Data as IList;
                    if (dataList.Count > 0)
                    {
                        type = dataList[0].GetType();
                        var result = Activator.CreateInstance(output.Data.GetType()) as IList;
    
                        foreach (var temp in dataList)
                        {
                            var instance = Activator.CreateInstance(type);
                            foreach (var property in type.GetProperties())
                            {
                                if (!((IList) param).Contains(property.Name)) continue;
                                object value = property.GetValue(temp, null);
                                property.SetValue(instance, value);
                            }
                            result.Add(instance);
                        }
                        output.Data = result;
                    }
                }

    上面的一些代码运行起来效率比原生5000次只低不50-100ms,完全可以接受。但是会发现序列化后我们不想要被序列化的属性值变成了null。研究了一下问题出在

    var instance = Activator.CreateInstance(type);  这里,因为我们创建的还是那个类嘛。。属性也无法被砍掉。想了想这里可以用动态类型去实现。

                    type = obj.GetType();
                    var result = new ExpandoObject() as IDictionary<string, Object>;
                    foreach (var property in type.GetProperties())
                    {
                        if (retain)
                        {
                            if (!((IList)props).Contains(property.Name.ToLower())) continue;
                        }
                        else
                        {
                            if (((IList)props).Contains(property.Name.ToLower())) continue;
                        }
                        object value = property.GetValue(obj, null);
                        result.Add(property.Name, value);
    
                    }
                    obj = result;

    实验了一下对效果非常满意,另外一个有意思的事情是效率竟然比原生要快上一倍

    image

    这张图直接进行序列化

    image

    这张图是用过滤了属性

    image

    下面除上完整的代码,有需要的同学可以进行使用

    using System;
    using System.Collections;
    using System.Collections.Generic;
    using System.Dynamic;
    using Tool.Response;
    
    namespace MvcCustommade
    {
        public class LimitPropsContractResolver
        {
    
    
    
            private static object Filtered(object obj, string[] props, bool retain)
            {
    
                if (obj == null)
                    return null;
                Type type = null;
                if (obj is IList)
                {
                    var dataList = obj as IList;
                    if (dataList.Count > 0)
                    {
                        type = dataList[0].GetType();
                        List<IDictionary<string, object>> result = new List<IDictionary<string, object>>();
    
                        foreach (var temp in dataList)
                        {
                            var instance = new ExpandoObject() as IDictionary<string, Object>;
                            foreach (var property in type.GetProperties())
                            {
                                if (retain)
                                {
                                    if (!((IList)props).Contains(property.Name.ToLower())) continue;
                                }
                                else
                                {
                                    if (((IList)props).Contains(property.Name)) continue;
                                }
    
                                object value = property.GetValue(temp, null);
                                instance.Add(property.Name, value);
                            }
                            result.Add(instance);
                        }
                        obj = result;
                    }
                }
                else
                {
    
                    type = obj.GetType();
                    var result = new ExpandoObject() as IDictionary<string, Object>;
                    foreach (var property in type.GetProperties())
                    {
                        if (retain)
                        {
                            if (!((IList)props).Contains(property.Name.ToLower())) continue;
                        }
                        else
                        {
                            if (((IList)props).Contains(property.Name.ToLower())) continue;
                        }
                        object value = property.GetValue(obj, null);
                        result.Add(property.Name, value);
    
                    }
                    obj = result;
                }
    
                return obj;
            }
    
            /// <summary>
            /// 过滤无用的属性
            /// </summary>
            /// <param name="output">返回的数据</param>
            /// <param name="props">过滤的属性数组</param>
            /// <param name="retain">过滤的数组是包含还是不包含</param>
            public static object CreateProperties(object output, string[] props, bool retain)
            {
                if (props == null || props.Length <= 0)
                {
                    return output;
                }
                if (output == null)
                {
                    return null;
                }
                for (int i = 0; i < props.Length; i++)
                {
                    props[i] = props[i].ToLower();
                }
                if (output is OutputModel)
                {
                    var outputModel = output as OutputModel;
                    if (outputModel.Data == null)
                    {
                        return outputModel;
                    }
                    outputModel.Data = Filtered(outputModel.Data, props, retain);
                    return outputModel;
    
                    #region 初始版
    
                    //Type type = null;
    
                    //if (outputModle.Data is IList)
                    //{
                    //    var ss = outputModle.Data as IList;
                    //    if (ss.Count > 0)
                    //    {
                    //        type = ss[0].GetType();
                    //        List<IDictionary<string, object>> result = new List<IDictionary<string, object>>();
    
                    //        foreach (var temp in ss)
                    //        {
                    //            var instance = new ExpandoObject() as IDictionary<string, Object>;
                    //            foreach (var property in type.GetProperties())
                    //            {
                    //                if (retain)
                    //                {
                    //                    if (!((IList)props).Contains(property.Name)) continue;
                    //                }
                    //                else
                    //                {
                    //                    if (((IList)props).Contains(property.Name)) continue;
                    //                }
    
                    //                object value = property.GetValue(temp, null);
                    //                instance.Add(property.Name, value);
                    //            }
                    //            result.Add(instance);
                    //        }
                    //        outputModle.Data = result;
                    //    }
                    //}
                    //else
                    //{
                    //    if (outputModle.Data == null) return;
                    //    type = outputModle.Data.GetType();
                    //    var result = new ExpandoObject() as IDictionary<string, Object>;
                    //    foreach (var property in type.GetProperties())
                    //    {
                    //        if (retain)
                    //        {
                    //            if (!((IList)props).Contains(property.Name)) continue;
                    //        }
                    //        else
                    //        {
                    //            if (((IList)props).Contains(property.Name)) continue;
                    //        }
                    //        object value = property.GetValue(outputModle.Data, null);
                    //        result.Add(property.Name, value);
    
                    //    }
                    //    outputModle.Data = result;
                    //} 
    
                    #endregion
                }
                else
                {
                    output = Filtered(output, props, retain);
                    return output;
                }
    
            }
        }
    }
  • 相关阅读:
    调用匿名函数的骚操作
    angular,vue,react的父子通信
    JavaScript高阶函数的应用
    图解javascript中this指向
    前端开发必备
    MEAN-全栈javascript开发框架
    MongoDB初识
    ES6新特性概览
    为什么是link-visited-hover-active
    CSS布局 — 圣杯布局与双飞翼布局
  • 原文地址:https://www.cnblogs.com/LiangSW/p/5974412.html
Copyright © 2011-2022 走看看