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?
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.
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.
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.
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);
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;
}
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.
Versuchen Sie es mit der IgnoreDataMember
-Eigenschaft
public class Foo
{
[IgnoreDataMember]
public int Id { get; set; }
public string Name { get; set; }
}
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,
}),
}));
}
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());
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();
}
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 POST
ing 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);
}