防止在Web API中序列化属性

use*_*271 165 c# asp.net asp.net-mvc asp.net-web-api

我正在使用MVC 4 Web API和asp.net web forms 4.0来构建一个rest API.它工作得很好:

[HttpGet]
public HttpResponseMessage Me(string hash)
{
    HttpResponseMessage httpResponseMessage;
    List<Something> somethings = ...

    httpResponseMessage = Request.CreateResponse(HttpStatusCode.OK, 
                                 new { result = true, somethings = somethings });

    return httpResponseMessage;
}
Run Code Online (Sandbox Code Playgroud)

现在我需要阻止一些属性被序列化.我知道我可以在列表上使用一些LINQ并只获取我需要的属性,通常这是一个很好的方法,但在目前的情况下,something对象太复杂了,我需要在不同的方法中使用不同的属性集,所以它是在运行时,更容易标记要忽略的每个属性.

有没有办法做到这一点?

cuo*_*gle 216

的ASP.NET Web API使用Json.Net的默认格式,所以如果你的应用程序仅仅只使用JSON作为数据格式,你可以用[JsonIgnore]忽略属性序列:

public class Foo
{
    public int Id { get; set; }
    public string Name { get; set; }

    [JsonIgnore]
    public List<Something> Somethings { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

但是,这种方式不支持XML格式.因此,如果您的应用程序必须支持更多XML格式(或仅支持XML),而不是使用Json.Net,则应使用[DataContract]支持JSON和XML的应用程序:

[DataContract]
public class Foo
{
    [DataMember]
    public int Id { get; set; }
    [DataMember]
    public string Name { get; set; }

    //Ignore by default
    public List<Something> Somethings { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

为了更加了解,您可以阅读官方文章.


小智 111

根据ASP.NET Web API中的Web API文档页面JSON和XML序列化,可以显式阻止对属性进行序列化,您可以将其[JsonIgnore]用于Json序列化程序或[IgnoreDataMember]默认的XML序列化程序.

但是在测试中我注意到它[IgnoreDataMember]阻止了XML和Json请求的序列化,因此我建议使用它而不是装饰具有多个属性的属性.

  • 可悲的是,`[IgnoreDataMember]`似乎不适用于延迟加载的EF 6代理对象(虚拟属性).但是,`[DataContract]`和`[DataMember]`呢. (16认同)
  • 这是更好的答案.它涵盖了具有一个属性的XML和JSON. (2认同)

CBo*_*ono 29

您可以采用"选择加入"方式,而不是让所有内容都默认为序列化.在此方案中,只允许序列化您指定的属性.你这样做与DataContractAttributeDataMemberAttribute,在发现System.Runtime.Serialization命名空间.

DataContactAttribute被应用到类,并DataMemberAttribute适用于你想成为系列化每个成员:

[DataContract]
public class MyClass {

  [DataMember]
  public int Id { get; set;} // Serialized

  [DataMember]
  public string Name { get; set; } // Serialized

  public string DontExposeMe { get; set; } // Will not be serialized
}
Run Code Online (Sandbox Code Playgroud)

我敢说这是一种更好的方法,因为它迫使你做出明确的决定,决定通过序列化做什么或不做什么.它还允许您的模型类自己生活在一个项目中,而不依赖于JSON.net只是因为其他地方您碰巧使用JSON.net将它们序列化.

  • 与 .Net Core 一起开箱即用的隐藏继承成员的唯一方法。适用于 XML 和 Json 序列化。点赞 (2认同)

joy*_*ym8 19

这对我有用:创建一个自定义合约解析器,它有一个名为AllowList of string array type的公共属性.在您的操作中,根据操作需要返回的内容修改该属性.

1.创建自定义合同解析程序:

public class PublicDomainJsonContractResolverOptIn : DefaultContractResolver
{
    public string[] AllowList { get; set; }

    protected override IList<JsonProperty> CreateProperties(Type type, MemberSerialization memberSerialization)
    {
        IList<JsonProperty> properties = base.CreateProperties(type, memberSerialization);

        properties = properties.Where(p => AllowList.Contains(p.PropertyName)).ToList();
        return properties;
    }
}
Run Code Online (Sandbox Code Playgroud)

2.使用自定义合同解析器

[HttpGet]
public BinaryImage Single(int key)
{
    //limit properties that are sent on wire for this request specifically
    var contractResolver = Configuration.Formatters.JsonFormatter.SerializerSettings.ContractResolver as PublicDomainJsonContractResolverOptIn;
    if (contractResolver != null)
        contractResolver.AllowList = new string[] { "Id", "Bytes", "MimeType", "Width", "Height" };

    BinaryImage image = new BinaryImage { Id = 1 };
    //etc. etc.
    return image;
}
Run Code Online (Sandbox Code Playgroud)

这种方法允许我允许/禁止特定请求,而不是修改类定义.如果您不需要XML序列化,请不要忘记将其关闭App_Start\WebApiConfig.cs的API中如果客户端请求xml而不是json,您的API将返回被阻止的属性.

//remove xml serialization
var appXmlType = config.Formatters.XmlFormatter.SupportedMediaTypes.FirstOrDefault(t => t.MediaType == "application/xml");
config.Formatters.XmlFormatter.SupportedMediaTypes.Remove(appXmlType);
Run Code Online (Sandbox Code Playgroud)

  • 更糟糕的是,香港专业教育学院对此进行了测试,并且合约解析器缓存了createproperties调用.这个答案充其量是天真的,最坏的是危险的. (2认同)

fox*_*ard 17

我将告诉你两种方法来实现你想要的:

第一种方法:使用JsonProperty属性装饰您的字段,以便跳过该字段的序列化(如果它为null).

public class Foo
{
    public int Id { get; set; }
    public string Name { get; set; }

    [JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
    public List<Something> Somethings { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

第二种方式:如果您正在与一些复杂的场景进行协商,那么您可以使用Web Api约定("ShouldSerialize"),以便根据某些特定逻辑跳过该字段的序列化.

public class Foo
{
    public int Id { get; set; }
    public string Name { get; set; }

    public List<Something> Somethings { get; set; }

    public bool ShouldSerializeSomethings() {
         var resultOfSomeLogic = false;
         return resultOfSomeLogic; 
    }
}
Run Code Online (Sandbox Code Playgroud)

WebApi使用JSON.Net并使用反射进行序列化,因此当它检测到(例如)ShouldSerializeFieldX()方法时,名称为FieldX的字段将不会被序列化.


Tim*_*han 16

我迟到了游戏,但是一个匿名的对象可以解决这个问题:

[HttpGet]
public HttpResponseMessage Me(string hash)
{
    HttpResponseMessage httpResponseMessage;
    List<Something> somethings = ...

    var returnObjects = somethings.Select(x => new {
        Id = x.Id,
        OtherField = x.OtherField
    });

    httpResponseMessage = Request.CreateResponse(HttpStatusCode.OK, 
                                 new { result = true, somethings = returnObjects });

    return httpResponseMessage;
}
Run Code Online (Sandbox Code Playgroud)


Kav*_*avi 10

尝试使用IgnoreDataMember属性

public class Foo
    {
        [IgnoreDataMember]
        public int Id { get; set; }
        public string Name { get; set; }
    }
Run Code Online (Sandbox Code Playgroud)


小智 8

只需添加以下内容即可正常工作: [IgnoreDataMember]

在 propertyp 之上,例如:

public class UserSettingsModel
{
    public string UserName { get; set; }
    [IgnoreDataMember]
    public DateTime Created { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

这适用于 ApiController。编码:

[Route("api/Context/UserSettings")]
    [HttpGet, HttpPost]
    public UserSettingsModel UserSettings()
    {
        return _contextService.GetUserSettings();
    }
Run Code Online (Sandbox Code Playgroud)


tsu*_*980 5

几乎与greatbear302的答案相同,但我根据请求创建了ContractResolver.

1)创建自定义ContractResolver

public class MyJsonContractResolver : DefaultContractResolver
{
    public List<Tuple<string, string>> ExcludeProperties { get; set; }

    protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
    {
        JsonProperty property = base.CreateProperty(member, memberSerialization);

        if (ExcludeProperties?.FirstOrDefault(
            s => s.Item2 == member.Name && s.Item1 == member.DeclaringType.Name) != null)
        {
            property.ShouldSerialize = instance => { return false; };
        }

        return property;
    }
}
Run Code Online (Sandbox Code Playgroud)

2)使用自定义合同解析器

public async Task<IActionResult> Sites()
{
    var items = await db.Sites.GetManyAsync();

    return Json(items.ToList(), new JsonSerializerSettings
    {
        ContractResolver = new MyJsonContractResolver()
        {
            ExcludeProperties = new List<Tuple<string, string>>
            {
                Tuple.Create("Site", "Name"),
                Tuple.Create("<TypeName>", "<MemberName>"),
            }
        }
    });
}
Run Code Online (Sandbox Code Playgroud)

编辑:

它没有按预期工作(根据请求隔离解析器).我将使用匿名对象.

public async Task<IActionResult> Sites()
{
    var items = await db.Sites.GetManyAsync();

    return Json(items.Select(s => new
    {
        s.ID,
        s.DisplayName,
        s.Url,
        UrlAlias = s.Url,
        NestedItems = s.NestedItems.Select(ni => new
        {
            ni.Name,
            ni.OrdeIndex,
            ni.Enabled,
        }),
    }));
}
Run Code Online (Sandbox Code Playgroud)


ken*_*ner 4

您也许可以使用 AutoMapper 并使用映射.Ignore(),然后发送映射的对象

CreateMap<Foo, Foo>().ForMember(x => x.Bar, opt => opt.Ignore());
Run Code Online (Sandbox Code Playgroud)