2012-08-07 62 views
112

我正在使用mvc 4 web api和asp.net webforms 4.0来构建休息api。它的工作很好:防止在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

您可以将ScriptIgnore添加到属性。查看此问题http://stackoverflow.com/questions/10169648/how-to-exclude-property-from-json-serialization – atbebtg 2012-08-07 17:44:12

回答

3

您可能能够使用AutoMapper和使用.Ignore()映射,然后发送映射对象

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

的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,你应该使用[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; } 
} 

更多了解,可以阅读official article

+0

我想我将不得不找到一种方法来添加和删除运行时使用jsonignore属性。 – user1330271 2012-08-07 17:57:49

+0

工作就像一个魅力!谢谢:) – 2016-05-19 14:07:38

+0

JSONIgnore属性不支持XML响应,为什么会很伤心? – Mukus 2016-11-08 05:29:42

95

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

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

+2

这是更好的答案。它包含具有一个属性的XML和JSON。 – Oliver 2013-08-21 14:13:36

+15

不幸的是'[IgnoreDataMember]'似乎不适用于惰性加载的EF 6代理对象(虚拟属性)。但是,[DataContract]和[DataMember]可以。 – Nick 2014-08-01 15:30:25

21

而不是让一切默认序列化,您可以采取“选择”的方法。在这种情况下,只有您指定的属性才允许被序列化。您可以使用名称空间System.Runtime.Serialization中的DataContractAttributeDataMemberAttribute执行此操作。

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对它们进行序列化。

+1

这是使用.Net Core隐藏继承成员的唯一方法。适用于XML和Json序列化。 Kudos – Piou 2016-10-20 22:14:54

+0

我需要相同的funcionality,但是包含或排除属性取决于哪个api方法被调用,即不同的api调用需要不同的数据。任何建议 – 2018-02-15 03:08:35

18

这对我有用:创建一个自定义合约解析器,它具有一个名为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); 
+0

一些必须改变与新版本,但我无法得到这个工作。在修改解析器时,我可以通过执行'new'而不是'as'来实现它。由于某种原因,JsonContractResolver类型不兼容。做新事的问题是,它会覆盖全部而不仅仅是一个。 – 2014-02-05 18:01:38

+0

我管理通过使用Request.CreateResponse()方法,其接收MediaTypeFormatter,像这样得到这个工作: VAR jsonMediaTypeFormatter =新JsonMediaTypeFormatter { SerializerSettings =新JsonSerializerSettings { ContractResolver =新PublicDomainJsonContractResolverOptIn {允许列表=新的字符串[ ] {“Id”,“Bytes”,“MimeType”,“Width”,“Height”}} } }; return Request.CreateResponse(HttpStatusCode.OK,image,jsonMediaTypeFormatter); – Paul 2014-09-30 12:28:43

+0

如果我们还希望在XML响应中忽略阻塞的属性,该怎么办? – 2015-12-06 16:14:15

12

我迟到的游戏,而是一个匿名对象会做的伎俩:

[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

尝试使用IgnoreDataMember财产

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

我会告诉你2种方法来实现你想要什么:

第一种方式:用JsonPro装饰你的领域perty属性,以便在该字段为空时跳过该字段的序列化。

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的字段将不被序列化。

+0

这不是由web api完成的,web api默认使用Json.NET进行序列化。这个过程由Json.NET而不是web api完成 – dotctor 2016-01-02 11:40:22

0

由于某种原因[IgnoreDataMember]并不总是适用于我,我有时会得到StackOverflowException(或类似)。

[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,并将其转换已经收到到后:所以不是(或除了)我一直在使用看起来像这样的模式时POST荷兰国际集团在Objects我的API开始由内置序列化程序引起的问题,有时会在解析对象时导致无限循环。

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

这可能是值得指出的是,它是一个的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); 
} 
4

几乎同样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, 
     }), 
    })); 
}