it-swarm.com.de

verhindern, dass die Eigenschaft in der Web-API serialisiert wird

Ich verwende eine MVC 4-Web-API und asp.net-Webformulare 4.0, um eine Rest-API zu erstellen. Es funktioniert großartig:

[HttpGet]
public HttpResponseMessage Me(string hash)
{
    HttpResponseMessage httpResponseMessage;
    List<Something> somethings = ...

    httpResponseMessage = Request.CreateResponse(HttpStatusCode.OK, 
                                 new { result = true, somethings = somethings });

    return httpResponseMessage;
}

Nun muss ich verhindern, dass einige Eigenschaften serialisiert werden. Ich weiß, dass ich etwas LINQ über die Liste verwenden kann und nur die Eigenschaften bekomme, die ich brauche. Im Allgemeinen ist dies ein guter Ansatz. Im vorliegenden Szenario ist das something-Objekt jedoch zu komplex und ich brauche also unterschiedliche Eigenschaften in verschiedenen Methoden Es ist einfacher, zur Laufzeit jede zu ignorierende Eigenschaft zu markieren.

Gibt es eine Möglichkeit, dies zu tun?

135
user1330271

Die ASP.NET-Web-API verwendet Json.Net als Standardformatierer. Wenn Ihre Anwendung also nur JSON als Datenformat verwendet, können Sie _[JsonIgnore]_ verwenden, um die Eigenschaft für die Serialisierung zu ignorieren:

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

    [JsonIgnore]
    public List<Something> Somethings { get; set; }
}
_

Auf diese Weise wird das XML-Format jedoch nicht unterstützt. Wenn Ihre Anwendung das XML-Format mehr unterstützen muss (oder nur XML unterstützt), sollten Sie _Json.Net_ verwenden, das _[DataContract]_ unterstützt JSON und 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; }
}
_

Zum besseren Verständnis können Sie den offiziellen Artikel lesen.

208
cuongle

Entsprechend der Web-API-Dokumentationsseite JSON- und XML-Serialisierung in ASP.NET Web API können Sie die Serialisierung einer Eigenschaft explizit verhindern, indem Sie entweder [JsonIgnore] für den Json-Serializer oder [IgnoreDataMember] für den Standard-XML-Serializer verwenden.

Beim Testen habe ich jedoch festgestellt, dass [IgnoreDataMember] die Serialisierung sowohl für XML- als auch für Json-Anforderungen verhindert. Daher würde ich empfehlen, dies zu verwenden, anstatt eine Eigenschaft mit mehreren Attributen zu dekorieren.

104
Michael Mason

Anstatt alles standardmäßig serialisiert zu lassen, können Sie den "Opt-In" -Ansatz wählen. In diesem Szenario dürfen nur die von Ihnen angegebenen Eigenschaften serialisiert werden. Dies tun Sie mit der DataContractAttribute und DataMemberAttribute , die sich im Namespace System.Runtime.Serialization befindet.

Die Variable DataContactAttribute wird auf die Klasse angewendet, und die Variable DataMemberAttribute wird auf jedes Member angewendet, das Sie serialisieren möchten:

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

Ich wage zu sagen, dass dies ein besserer Ansatz ist, weil Sie dazu gezwungen werden, explizit zu entscheiden, was durch Serialisierung erreicht wird oder nicht. Außerdem können Ihre Modellklassen selbst in einem Projekt leben, ohne von JSON.net abhängig zu sein, nur weil Sie sie irgendwo anders mit JSON.net serialisieren.

25
CBono

Dies funktionierte für mich: Erstellen Sie einen benutzerdefinierten Vertragsauflöser, der eine öffentliche Eigenschaft namens AllowList vom Typ String-Array aufweist. Ändern Sie diese Eigenschaft in Ihrer Aktion abhängig davon, was die Aktion zurückgeben muss.

1. Erstellen Sie einen benutzerdefinierten Vertragslöser:

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. Verwenden Sie den benutzerdefinierten Vertragslöser in Aktion

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

Dieser Ansatz ermöglichte es mir, spezifische Anforderungen zuzulassen/abzulehnen, anstatt die Klassendefinition zu ändern. Wenn Sie keine XML-Serialisierung benötigen, vergessen Sie nicht, sie in Ihrem App_Start\WebApiConfig.cs zu deaktivieren, oder Ihre API gibt blockierte Eigenschaften zurück, wenn der Client XML anstelle von Json anfordert.

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

Ich bin spät dran, aber anonyme Objekte würden den Trick tun:

[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;
}
15
Tim Hoolihan

Ich zeige Ihnen 2 Wege, um das zu erreichen, was Sie wollen:

Erster Weg: Dekorieren Sie Ihr Feld mit dem JsonProperty-Attribut, um die Serialisierung dieses Feldes zu überspringen, falls es null ist.

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

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

Zweiter Weg: Wenn Sie mit komplexen Szenarien verhandeln, können Sie die Web-Api-Konvention ("ShouldSerialize") verwenden, um die Serialisierung dieses Feldes in Abhängigkeit von einer bestimmten Logik zu überspringen.

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 verwendet JSON.Net und Reflection für die Serialisierung. Wenn also die ShouldSerializeFieldX () -Methode erkannt wird, wird das Feld mit dem Namen FieldX nicht serialisiert.

14
foxhard

Versuchen Sie es mit der IgnoreDataMember-Eigenschaft

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

Fast genauso wie die Antwort von greatbear302, aber ich erstelle den ContractResolver pro Anfrage.

1) Erstellen Sie einen benutzerdefinierten 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) Verwenden Sie den benutzerdefinierten Vertragslöser in Aktion

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

Bearbeiten:

Es hat nicht wie erwartet funktioniert (Resolver pro Anforderung isolieren). Ich verwende anonyme Objekte.

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

Möglicherweise können Sie AutoMapper verwenden, die Zuordnung .Ignore() verwenden und dann das zugeordnete Objekt senden

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

Funktioniert einwandfrei, indem einfach Folgendes hinzugefügt wird: [IgnoreDataMember]

Oben auf dem propertyyp, wie:

public class UserSettingsModel
{
    public string UserName { get; set; }
    [IgnoreDataMember]
    public DateTime Created { get; set; }
}

Dies funktioniert mit ApiController. Der Code:

[Route("api/Context/UserSettings")]
    [HttpGet, HttpPost]
    public UserSettingsModel UserSettings()
    {
        return _contextService.GetUserSettings();
    }
2
Dannejaha

Aus irgendeinem Grund funktioniert [IgnoreDataMember] nicht immer für mich und manchmal bekomme ich StackOverflowException (oder ähnliches). Stattdessen (oder zusätzlich) habe ich damit begonnen, ein Muster zu verwenden, das ungefähr so ​​aussieht, als POSTing in Objects für meine 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);
}

Grundsätzlich übergebe ich eine JObject und konvertiere sie, nachdem sie empfangen wurde, um Probleme zu vermeiden, die durch den eingebauten Serializer verursacht werden und manchmal eine unendliche Schleife beim Parsen der Objekte verursachen.

Wenn jemand einen Grund kennt, dass dies in irgendeiner Weise eine schlechte Idee ist, lass es mich wissen.

Es kann erwähnenswert sein, dass der folgende Code für eine EntityFramework-Klasseneigenschaft das Problem verursacht (wenn zwei Klassen aufeinander bezogen sind):

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