剑轩

.net webapi Newtonsoft.Json返回需要的字段,忽略某些字段,修改字段名,动态返回需要的字段等

电脑版发表于:2019/11/12 14:33

有时候我们在写webapi的时候,不想把正常用的对象所有字段都返回,我们需要进行一些筛选,忽略某些字段,大概说一下几种方法


方法一:给不同需求单独写查询,提供不同的数据对象

单独写一个model,然后单独写一个查询,在通过接口提供出去

这种方法效率比较高,不需要单独解析一下,就是代码量多一点,适用于变化不大的情况。

当然也可能是这个接口是需要依赖与另外一个接口返回的数据,道理是一样,就是对数据源加工后进去返回,至于数据源是数据库还是缓存还是其他接口提供的数据不影响我们数据加工几种方法的使用


方法二:使用对象映射

单独写一个model,把原有model赋值到另外的对象,使用automapper或者自己通过反射映射一下对象即可 这样写法要通过反射转化一遍,效率会慢一点,但是可以共用一个查询方法。

想要修改字段名可以自己弄一个映射关系,用特性或者动态映射都可以,automapper映射一个名字还是比较方便的

          

方法三:通过Newtonsoft.Json在model中配置忽略某些值即可

 通过JsonIgnore特性配置即可。

 如下:我们可以忽略AClass与AClassId字段

 

 因为net core中默认就是使用的Newtonsoft.Json序列化json所以这样就可以了,不需要借助其他东西做其他操作

 效果如下:

  没有忽略前

             

  忽略后,会看到字段没有了

             

这种写法不需要借助其他第三方的东西,配置一下即可。但是还是会影响一些效率,json序列化的时候会进行判断,但是效率优于自己写反射解析,而且不需要自己重新写一个model。不过灵活性差一点,如果有两个用户需要返回不同的字段,只有这一种方式就不是很好的实现


想要改变一下名字也是很方便的,直接使用JsonProperty即可

如下我想把SContent换成Summary

用JsonProperty特性指定一下即可:

//文章摘要信息
[JsonProperty(PropertyName = "Summary")]
public string SContent { get; set; }

 我们可以看到原有的名字已经被修改成Summary了



方法四:通过生成动态类型ExpandoObject

这种写法可以根据不传递的需要返回的字段,或者传递想要忽略的字段动态构建一个类型,然后返回前台,这种方式很灵活,适用于用户需求变化大的情况,或者说某些特殊的数据返回需求,可以和第三种方法结合使用,一个提供标准化服务一个提供特殊化服务。

这种写法由于会使用到反射和动态构建一个ExpandoObject所以效率会不如直接返回model和第三种配置json忽略的写法,而且返回值还必须是ExpandoObject,如果我们model是一个很多对象组合在一起的会降低一点代码可读性。

具体做法参考博客:http://www.tnblog.net/hb/article/details/2788

他这里没有提供忽略某些字段的写法,我来补充一个:

public class LimitPropsContractResolver : DefaultContractResolver
    {
        string[] props = null;
        public enum LimitType { Contains, Ignore }

        public LimitType limitType;

        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="props">传入的属性数组</param>
        /// <param name="retain">true:表示props是需要保留的字段  false:表示props是要排除的字段</param>
        public LimitPropsContractResolver(string[] props, LimitType limitType = LimitType.Contains)
        {
            //指定要序列化属性的清单
            this.props = props;
            this.limitType = limitType;
        }

        protected override IList<JsonProperty> CreateProperties(Type type, MemberSerialization memberSerialization)
        {
            IList<JsonProperty> list = base.CreateProperties(type, memberSerialization);
            //只保留清单有列出的属性
            return list.Where(p =>
            {
                if (limitType == LimitType.Contains)
                {
                    //匹配的时候为了不区分大小写
                    return props.FirstOrDefault(a => a.ToLower() == p.PropertyName.ToLower()) != null;
                }
                else
                {
                    return props.FirstOrDefault(a => a.ToLower() == p.PropertyName.ToLower()) == null;
                }
            }).ToList();
        }
    }

用法,比如要忽略aClass与aClassId字段:

#region 忽略aClass与aClassId字段
var result = art.ToDynamicIgnore("aClass,aClassId");
returnModel.Value.dto_Article = result;
#endregion


补充方法三.1:通过Newtonsoft.Json实现动态需要返回的字段

有些时候我们需要动态的返回字段给不同的用户,比如小明用户需要A,B,C三个字段,小红需要C,M,D。虽然可以使用第四种方法实现,但是我们可以直接通过Newtonsoft.Json来实现,不需要重新写一个model,也不需要动态构建一个ExpandoObject。

通过扩展DefaultContractResolver来实现:

public class LimitPropsContractResolver : DefaultContractResolver
    {
        string[] props = null;
        public enum LimitType { Contains, Ignore }

        public LimitType limitType;

        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="props">传入的属性数组</param>
        /// <param name="retain">true:表示props是需要保留的字段  false:表示props是要排除的字段</param>
        public LimitPropsContractResolver(string[] props, LimitType limitType = LimitType.Contains)
        {
            //指定要序列化属性的清单
            this.props = props;
            this.limitType = limitType;
        }

        protected override IList<JsonProperty> CreateProperties(Type type, MemberSerialization memberSerialization)
        {
            IList<JsonProperty> list = base.CreateProperties(type, memberSerialization);
            //只保留清单有列出的属性
            return list.Where(p =>
            {
                if (limitType == LimitType.Contains)
                {
                    //匹配的时候为了不区分大小写
                    return props.FirstOrDefault(a => a.ToLower() == p.PropertyName.ToLower()) != null;
                }
                else
                {
                    return props.FirstOrDefault(a => a.ToLower() == p.PropertyName.ToLower()) == null;
                }
            }).ToList();
        }
    }

比如我们只想取 title和aClass两个字段如下即可

//动态查询想要的字段
JsonSerializerSettings jsetting = new JsonSerializerSettings();
jsetting.ContractResolver = new LimitPropsContractResolver(new string[] { "title", "aClass" });
string jsonstr = JsonConvert.SerializeObject(art, Formatting.Indented, jsetting);

效果如下:

如果想要忽略的话,也很简单,通过自己封装的枚举指定一下: LimitType.Ignore

//动态忽略想要的字段
JsonSerializerSettings jsetting = new JsonSerializerSettings();
jsetting.ContractResolver = new LimitPropsContractResolver(new string[] { "id", "title", "aClass" }, LimitType.Ignore);
string jsonstr = JsonConvert.SerializeObject(art, Formatting.Indented, jsetting);


补充方法三.2:通过Newtonsoft.Json实现动态修改名字

有时候我们想要给不同的用户或者客户端提供不同的名字,又比如不同前台的组件需要不同格式的数据,我们可以动态的修改名字就不用为每个都重新定义一个新的模型。

和修改返回字段一样的去重写DefaultContractResolver

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

使用:

JsonSerializerSettings jsetting = new JsonSerializerSettings();
//jsetting.ContractResolver = new LimitPropsContractResolver(new string[] { "title", "aClass" }, LimitType.Contains);
Dictionary<string, string> dictProp = new Dictionary<string, string> { { "Id", "MyId" }, { "Title", "MyTitle" } };
jsetting.ContractResolver = new PropsContractResolver(dictProp);
string jsonstr = JsonConvert.SerializeObject(art, Formatting.Indented, jsetting);

这里我们把Id修改成了MyId,Title修改成了MyTitle


补充方法三.3:可以把筛选字段和修改字段名字的统一起来

因为貌似json的配置只能指定一个,所以把两个方法组合起来一起用,可以同时作用

public class LimitPropsContractResolver : DefaultContractResolver
{
    string[] props = null;
    public enum LimitType { Contains, Ignore }

    public LimitType limitType;

    Dictionary<string, string> dict_props = null;

    /// <summary>
    /// 构造函数
    /// </summary>
    /// <param name="props">传入的属性数组</param>
    /// <param name="retain">true:表示props是需要保留的字段  false:表示props是要排除的字段</param>
    public LimitPropsContractResolver(string[] props, LimitType limitType = LimitType.Contains, Dictionary<string, string> dictPropertyName=null)
    {
        //指定要序列化属性的清单
        this.props = props;
        this.limitType = limitType;
        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);
        }
    }

    protected override IList<JsonProperty> CreateProperties(Type type, MemberSerialization memberSerialization)
    {
        IList<JsonProperty> list = base.CreateProperties(type, memberSerialization);
        //只保留清单有列出的属性
        return list.Where(p =>
        {
            if (limitType == LimitType.Contains)
            {
                //匹配的时候为了不区分大小写
                return props.FirstOrDefault(a => a.ToLower() == p.PropertyName.ToLower()) != null;
            }
            else
            {
                return props.FirstOrDefault(a => a.ToLower() == p.PropertyName.ToLower()) == null;
            }
        }).ToList();
    }
}

使用的时候要注意,因为两个组合起来使用了,取需要字段的时候应该是给修改名字之后的,这样就可以的

但是如果这样写就不行:


补充方法三.4:筛选字段和修改字段名字的同时可以实现json命名的配置

如下想要实现自带的驼峰法命名,直接把我们自定义的继承驼峰法命名那个即可

当然如果你想要让他支持多命名的也可以做成配置的方式



webapi运用自定义json格式请参考:

http://www.tnblog.net/aojiancc2/article/details/2831



tip:以上几种方法可以根本不同的业务场景选择一种或者几种方法使用





关于TNBLOG
TNBLOG,技术分享。技术交流:群号677373950
ICP备案 :渝ICP备18016597号-1
App store Android
精彩评论
{{item.replyName}}
{{item.content}}
{{item.time}}
{{subpj.replyName}}
@{{subpj.beReplyName}}{{subpj.content}}
{{subpj.time}}
猜你喜欢