我使用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
对象太复杂了,我需要一个不同的集合不同方法中的属性,因此在运行时更容易标记要忽略的每个属性。
有办法吗?
答案 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
命名空间中的DataMemberAttribute
和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
}
我敢说这是一种更好的方法,因为它会迫使你做出明确的决定,决定通过序列化做什么或不做什么。它还允许您的模型类自己生活在一个项目中,而不依赖于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。它是高性能的,不需要其他库依赖项。
样品(谢谢)
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]
将无法正常工作。