防止在Web API中序列化属性

时间:2012-08-07 17:38:07

标签: c# asp.net asp.net-mvc asp.net-web-api

我使用MVC 4网络API和asp.net网络表单4.0来构建其他API。它工作得很好:

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

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

    return httpResponseMessage;
}

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

有办法吗?

12 个答案:

答案 0 :(得分:197)

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

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

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

为了便于理解,您可以阅读official article

答案 1 :(得分:105)

根据Web API文档页面JSON and XML Serialization in ASP.NET Web API明确阻止对属性进行序列化,您可以使用[JsonIgnore]作为Json序列化程序,或[IgnoreDataMember]作为默认XML序列化程序。

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

答案 2 :(得分:26)

默认情况下,您可以采用“选择加入”方式,而不是让所有内容进行序列化。在此方案中,只允许序列化您指定的属性。您可以使用DataContractAttribute命名空间中的DataMemberAttributeSystem.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
}

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

答案 3 :(得分: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;
    }
}

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

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

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

答案 4 :(得分:16)

我将向您展示两种方法来实现您的目标:

第一种方法:使用JsonProperty属性装饰您的字段,以便在该字段为空时跳过该字段的序列化。

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

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

第二种方式:如果您正在与某些复杂方案进行协商,那么您可以使用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; 
    }
}

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

答案 5 :(得分:15)

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

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

答案 6 :(得分:10)

尝试使用IgnoreDataMember属性

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

答案 7 :(得分: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;
    }
}

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

修改

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

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

答案 8 :(得分:3)

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

CreateMap<Foo, Foo>().ForMember(x => x.Bar, opt => opt.Ignore());

答案 9 :(得分:3)

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

在属性p的顶部,例如:

public class UserSettingsModel
{
    public string UserName { get; set; }
    [IgnoreDataMember]
    public DateTime Created { get; set; }
}

这适用于ApiController。代码:

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

答案 10 :(得分:0)

出于某种原因,[IgnoreDataMember]并不总是对我有用,有时我得StackOverflowException(或类似)。所以相反(或者另外)我在POST Objects到我的API时开始使用类似这样的模式:

[Route("api/myroute")]
[AcceptVerbs("POST")]
public IHttpActionResult PostMyObject(JObject myObject)
{
    MyObject myObjectConverted = myObject.ToObject<MyObject>();

    //Do some stuff with the object

    return Ok(myObjectConverted);
}

所以基本上我传入一个JObject并在收到它之后将其转换为由内置序列化程序引起的aviod问题,这有时在解析对象时会导致无限循环。

如果有人知道这是一个坏主意的原因,请告诉我。

值得注意的是,EntityFramework Class属性的以下代码会导致问题(如果两个类彼此引用):

[Serializable]
public partial class MyObject
{
   [IgnoreDataMember]
   public MyOtherObject MyOtherObject => MyOtherObject.GetById(MyOtherObjectId);
}

[Serializable]
public partial class MyOtherObject
{
   [IgnoreDataMember]
   public List<MyObject> MyObjects => MyObject.GetByMyOtherObjectId(Id);
}

答案 11 :(得分:0)

对于.NET Core 3.0及更高版本:

ASP.NET Core的默认JSON序列化程序现在为System.Text.Json,这是.NET Core 3.0中的新增功能。考虑尽可能使用System.Text.Json。它是高性能的,不需要其他库依赖项。

https://docs.microsoft.com/en-us/aspnet/core/migration/22-to-30?view=aspnetcore-3.1&tabs=visual-studio#newtonsoftjson-jsonnet-support

样品(谢谢)

using System.Text.Json.Serialization;

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

    [JsonIgnore]
    public List<Something> Somethings { get; set; }
}

如果您已经安装Newtonsoft.Json并选择使用它,则默认情况下,[JsonIgnore]将无法正常工作。