C# 防止属性在 Web API 中被序列化

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/11851207/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-09 19:30:15  来源:igfitidea点击:

prevent property from being serialized in web API

c#asp.netasp.net-mvcasp.net-web-api

提问by user1330271

I'm using an MVC 4 web API and asp.net web forms 4.0 to build a rest API. It's working great:

我正在使用 MVC 4 Web API 和 asp.net Web 表单 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;
}

Now I need to prevent some properties to be serialized. I know I can use some LINQ over the list and get only the properties I need, and generally it's a good approach, but in the present scenario the somethingobject is too complex, and I need a different set of properties in different methods, so it's easier to mark, at runtime, each property to be ignored.

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

Is there a way to do that?

有没有办法做到这一点?

回答by kenwarner

You might be able to use AutoMapper and use the .Ignore()mapping and then send the mapped object

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

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

回答by cuongle

ASP.NET Web API uses Json.Netas default formatter, so if your application just only uses JSON as data format, you can use [JsonIgnore]to ignore property for serialization:

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

But, this way does not support XML format. So, in case your application has tosupport XML format more (or only support XML), instead of using Json.Net, you should use [DataContract]which supports both JSON and XML:

但是,这种方式不支持 XML 格式。因此,如果您的应用程序必须更多支持 XML 格式(或仅支持 XML),则Json.Net不应使用,而应使用[DataContract]which 支持 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; }
}

For more understanding, you can read the official article.

更多理解可以阅读官方文章

回答by Michael Mason

According to the Web API documentation page JSON and XML Serialization in ASP.NET Web APIto explicitly prevent serialization on a property you can either use [JsonIgnore]for the Json serializer or [IgnoreDataMember]for the default XML serializer.

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

However in testing I have noticed that [IgnoreDataMember]prevents serialization for both XML and Json requests, so I would recommend using that rather than decorating a property with multiple attributes.

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

回答by CBono

Instead of letting everythingget serialized by default, you can take the "opt-in" approach. In this scenario, only the properties you specify are allowed to be serialized. You do this with the DataContractAttributeand DataMemberAttribute, found in the System.Runtime.Serializationnamespace.

您可以采用“选择加入”方法,而不是让所有内容默认序列化。在这种情况下,只允许序列化您指定的属性。你这样做与DataContractAttributeDataMemberAttribute,在发现System.Runtime.Serialization命名空间。

The DataContactAttributeis applied to the class, and the DataMemberAttributeis applied to each member you want to be serialized:

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
}

Dare I say this is a better approach because it forces you to make explicit decisions about what will or will not make it through serialization. It also allows your model classes to live in a project by themselves, without taking a dependency on JSON.net just because somewhere else you happen to be serializing them with JSON.net.

我敢说这是一种更好的方法,因为它迫使您明确决定什么会或不会通过序列化实现。它还允许您的模型类单独存在于一个项目中,而不会仅仅因为您碰巧在其他地方使用 JSON.net 序列化它们而依赖于 JSON.net。

回答by joym8

This worked for me: Create a custom contract resolver which has a public property called AllowList of string array type. In your action, modify that property depending on what the action needs to return.

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

1.create a custom contract resolver:

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.use custom contract resolver in action

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

This approach allowed me to allow/disallow for specific request instead of modifying the class definition. And if you don't need XML serialization, don't forget to turn it off in your App_Start\WebApiConfig.csor your API will return blocked properties if the client requests xml instead of json.

这种方法允许我允许/禁止特定请求,而不是修改类定义。如果您不需要 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);

回答by Tim Hoolihan

I'm late to the game, but an anonymous objects would do the trick:

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

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

回答by Kavi

Try using IgnoreDataMemberproperty

尝试使用IgnoreDataMember属性

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

回答by foxhard

I will show you 2 ways to accomplish what you want:

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

First way: Decorate your field with JsonProperty attribute in order to skip the serialization of that field if it is null.

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

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

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

Second way: If you are negotiation with some complex scenarios then you could use the Web Api convention ("ShouldSerialize") in order to skip serialization of that field depending of some specific logic.

第二种方式:如果您正在协商一些复杂的场景,那么您可以使用 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 uses JSON.Net and it use reflection to serialization so when it has detected (for instance) the ShouldSerializeFieldX() method the field with name FieldX will not be serialized.

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

回答by Arg0n

For some reason [IgnoreDataMember]does not always work for me, and I sometimes get StackOverflowException(or similar). So instead (or in addition) i've started using a pattern looking something like this when POSTing in Objectsto my API:

由于某种原因[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);
}

So basically i pass in an JObjectand convert it after it has been recieved to aviod problems caused by the built-in serializer that sometimes cause an infinite loop while parsing the objects.

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

If someone know a reason that this is in any way a bad idea, please let me know.

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

It may be worth noting that it is the following code for an EntityFramework Class-property that causes the problem (If two classes refer to each-other):

可能值得注意的是,导致问题的是 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);
}

回答by tsu1980

Almost same as greatbear302's answer, but i create ContractResolver per request.

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

1) Create a custom 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) Use custom contract resolver in action

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

Edit:

编辑:

It didn't work as expected(isolate resolver per request). I'll use anonymous objects.

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

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