предотвратить сериализацию свойства в веб-API

Я использую веб-API MVC 4 и веб-формы 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 слишком сложен, и мне нужен другой набор свойств в разных методах, поэтому проще во время выполнения пометить каждое свойство, которое будет игнорироваться.

Есть способ сделать это?

Ответ 1

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

Для лучшего понимания вы можете прочитать официальную статью .

Ответ 2

В соответствии с страницей документации веб-API JSON и XML Serialization в ASP.NET Web API, чтобы явно предотвратить сериализацию в свойстве, которое вы можете использовать [JsonIgnore] для сериализатора Json или [IgnoreDataMember] для стандартного XML-сериализатора.

Однако при тестировании я заметил, что [IgnoreDataMember] предотвращает сериализацию для запросов XML и Json, поэтому я бы рекомендовал использовать это, а не украшать свойство несколькими атрибутами.

Ответ 3

Вместо того чтобы позволить всему сериализоваться по умолчанию, вы можете использовать подход "согласие". В этом случае сериализуются только те свойства, которые вы указали. Это можно сделать с помощью 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.

Ответ 4

Это сработало для меня: создайте пользовательский разрешитель контрактов, который имеет общедоступное свойство, называемое 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, или ваш API вернет заблокированные свойства, если клиент запрашивает xml вместо json.

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

Ответ 5

Я покажу вам два способа выполнить то, что вы хотите:

Первый способ: украсить поле атрибутом JsonProperty, чтобы пропустить сериализацию этого поля, если оно равно null.

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 не будет сериализовано.

Ответ 6

Я опаздываю на игру, но анонимные объекты будут делать трюк:

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

Ответ 7

Попробуйте использовать свойство IgnoreDataMember

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

Ответ 8

Почти так же, как 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>"),
            }
        }
    });
}

Edit:

Он не работал должным образом (изолируйте распознаватель на запрос). Я буду использовать анонимные объекты.

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

Ответ 9

Возможно, вы сможете использовать AutoMapper и использовать сопоставление .Ignore(), а затем отправить отображаемый объект

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

Ответ 10

Работает нормально, просто добавив: [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();
    }

Ответ 11

По какой-то причине [IgnoreDataMember] не всегда работает для меня, и я иногда получаю StackOverflowException (или подобное). Поэтому вместо этого (или, кроме того) я начал использовать шаблон, выглядящий примерно так, когда POST ing в 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, который вызывает проблему (если два класса относятся друг к другу):

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

Ответ 12

using Microsoft.AspNetCore.Mvc;
using Newtonsoft.Json;
using Newtonsoft.Json.Converters;


Asp.Net Core Web Api

[ModelMetadataType(typeof(IPerson))]
public partial class Person : IPerson
{

}

public partial class Person
{
    public string Name { get; set; }
    public string Surname { get; set; }
}

public interface IPerson
{
    [JsonIgnore]
    string Name { get; set; }
}