185

我正在使用 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;
}

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

有没有办法做到这一点?

4

12 回答 12

251

ASP.NET Web APIJson.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,您应该使用[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; }
}

更多了解,可以阅读官方文章

于 2012-08-07T17:56:06.770 回答
117

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

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

于 2012-10-05T07:22:31.043 回答
35

您可以采用“选择加入”的方法,而不是让所有内容默认序列化。在这种情况下,只允许序列化您指定的属性。您可以使用System.Runtime.Serialization命名空间中的DataContractAttributeand来执行此操作。DataMemberAttribute

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。

于 2013-08-16T12:53:05.690 回答
21

这对我有用:创建一个自定义合同解析器,它有一个名为 AllowList 的字符串数组类型的公共属性。在您的操作中,根据操作需要返回的内容修改该属性。

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);
于 2013-10-17T20:35:47.467 回答
19

我将向您展示两种方法来完成您想要的:

第一种方式:使用 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 的字段将不会被序列化。

于 2015-09-22T22:06:01.237 回答
17

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

[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;
}
于 2014-03-07T18:48:33.980 回答
13

尝试使用IgnoreDataMember属性

public class Foo
    {
        [IgnoreDataMember]
        public int Id { get; set; }
        public string Name { get; set; }
    }
于 2015-05-12T11:55:30.787 回答
9

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

在 propertyp 之上,例如:

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();
    }
于 2019-08-17T13:09:01.397 回答
6

几乎与 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,
        }),
    }));
}
于 2017-02-22T03:13:19.980 回答
4

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

CreateMap<Foo, Foo>().ForMember(x => x.Bar, opt => opt.Ignore());
于 2012-08-07T17:42:01.867 回答
4

对于 .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

样品(感谢cuongle)

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]将无法按预期工作。

于 2020-09-28T04:36:50.017 回答
0

由于某种原因[IgnoreDataMember]并不总是对我有用,我有时会得到StackOverflowException(或类似的)。所以相反(或另外)我已经开始使用一个看起来像这样的模式,当POSTObjects进入我的 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并在收到它后将其转换为避免由内置序列化程序引起的问题,这些问题有时会在解析对象时导致无限循环。

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

可能值得注意的是,导致问题的 EntityFramework 类属性的以下代码(如果两个类相互引用):

[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);
}
于 2016-02-18T08:04:24.013 回答