Hinweis
Für den Zugriff auf diese Seite ist eine Autorisierung erforderlich. Sie können versuchen, sich anzumelden oder das Verzeichnis zu wechseln.
Für den Zugriff auf diese Seite ist eine Autorisierung erforderlich. Sie können versuchen, das Verzeichnis zu wechseln.
In diesem Artikel werden die JSON- und XML-Formatierer in ASP.NET Web-API beschrieben.
In ASP.NET Web-API ist ein Medienformatierer ein Objekt, das:
- Lesen von CLR-Objekten aus einem HTTP-Nachrichtentext
- Schreiben von CLR-Objekten in einen HTTP-Nachrichtentext
Web-API stellt Medientypformatierer für JSON und XML bereit. Das Framework fügt diese Formatierer standardmäßig in die Pipeline ein. Clients können JSON oder XML im Accept-Header der HTTP-Anforderung anfordern.
Inhalt
JSON-Media-Type-Formatierer
JSON-Formatierung wird von der JsonMediaTypeFormatter-Klasse bereitgestellt. Standardmäßig verwendet JsonMediaTypeFormatter die Json.NET-Bibliothek zum Ausführen der Serialisierung. Json.NET ist ein Open Source-Projekt eines Drittanbieters.
Wenn Es Ihnen lieber ist, können Sie die JsonMediaTypeFormatter-Klasse so konfigurieren, dass anstelle von Json.NET der DataContractJsonSerializer verwendet wird. Legen Sie dazu die UseDataContractJsonSerializer-Eigenschaft auf "true" fest:
var json = GlobalConfiguration.Configuration.Formatters.JsonFormatter;
json.UseDataContractJsonSerializer = true;
JSON-Serialisierung
In diesem Abschnitt werden einige bestimmte Verhaltensweisen des JSON-Formatierers mithilfe des Standard-Json.NET Serialisierungsprogramms beschrieben. Dies ist nicht als umfassende Dokumentation der Json.NET Bibliothek gedacht; weitere Informationen finden Sie in der Json.NET Dokumentation.
Was wird serialisiert?
Standardmäßig sind alle öffentlichen Eigenschaften und Felder im serialisierten JSON enthalten. Wenn Sie eine Eigenschaft oder ein Feld weglassen möchten, versehen Sie sie mit dem JsonIgnore-Attribut .
public class Product
{
public string Name { get; set; }
public decimal Price { get; set; }
[JsonIgnore]
public int ProductCode { get; set; } // omitted
}
Wenn Sie einen "Opt-In"-Ansatz bevorzugen, schmücken Sie die Klasse mit dem DataContract-Attribut . Wenn dieses Attribut vorhanden ist, werden Member ignoriert, es sei denn, sie verfügen über den DataMember. Sie können dataMember auch verwenden, um private Mitglieder zu serialisieren.
[DataContract]
public class Product
{
[DataMember]
public string Name { get; set; }
[DataMember]
public decimal Price { get; set; }
public int ProductCode { get; set; } // omitted by default
}
Read-Only-Eigenschaften
Schreibgeschützte Eigenschaften werden standardmäßig serialisiert.
Datumsangaben
Standardmäßig schreibt Json.NET Datumsangaben im ISO 8601-Format . Datumsangaben in UTC (koordinierte Weltzeit) werden mit einem Suffix "Z" geschrieben. Datumsangaben in der lokalen Zeit enthalten einen Zeitzonenoffset. Beispiel:
2012-07-27T18:51:45.53403Z // UTC
2012-07-27T11:51:45.53403-07:00 // Local
Standardmäßig behält Json.NET die Zeitzone bei. Sie können dies überschreiben, indem Sie die DateTimeZoneHandling-Eigenschaft festlegen:
// Convert all dates to UTC
var json = GlobalConfiguration.Configuration.Formatters.JsonFormatter;
json.SerializerSettings.DateTimeZoneHandling = Newtonsoft.Json.DateTimeZoneHandling.Utc;
Wenn Sie das Microsoft JSON-Datumsformat ("\/Date(ticks)\/") anstelle von ISO 8601 verwenden möchten, legen Sie die DateFormatHandling-Eigenschaft für die Serialisierungseinstellungen fest:
var json = GlobalConfiguration.Configuration.Formatters.JsonFormatter;
json.SerializerSettings.DateFormatHandling
= Newtonsoft.Json.DateFormatHandling.MicrosoftDateFormat;
Einzug
Um eingezogenes JSON zu schreiben, legen Sie die Formatierungseinstellung auf "Formatting.Indented" fest:
var json = GlobalConfiguration.Configuration.Formatters.JsonFormatter;
json.SerializerSettings.Formatting = Newtonsoft.Json.Formatting.Indented;
Kamel casing
Wenn Sie JSON-Eigenschaftsnamen in CamelCase schreiben möchten, ohne das Datenmodell zu ändern, legen Sie den CamelCasePropertyNamesContractResolver für den Serializer fest.
var json = GlobalConfiguration.Configuration.Formatters.JsonFormatter;
json.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver();
Anonyme und schwach typisierte Objekte
Eine Aktionsmethode kann ein anonymes Objekt zurückgeben und in JSON serialisieren. Beispiel:
public object Get()
{
return new {
Name = "Alice",
Age = 23,
Pets = new List<string> { "Fido", "Polly", "Spot" }
};
}
Der Nachrichtentext der Antwort enthält den folgenden JSON-Code:
{"Name":"Alice","Age":23,"Pets":["Fido","Polly","Spot"]}
Wenn Ihre Web-API lose strukturierte JSON-Objekte von Clients empfängt, können Sie den Anforderungstext auf einen Newtonsoft.Json.Linq.JObject-Typ deserialisieren.
public void Post(JObject person)
{
string name = person["Name"].ToString();
int age = person["Age"].ToObject<int>();
}
In der Regel ist es jedoch besser, stark typierte Datenobjekte zu verwenden. Dann müssen Sie die Daten nicht selbst analysieren, und Sie erhalten die Vorteile der Modellüberprüfung.
Der XML-Serializer unterstützt keine anonymen Typen oder JObject-Instanzen . Wenn Sie diese Features für Ihre JSON-Daten verwenden, sollten Sie den XML-Formatierer aus der Pipeline entfernen, wie weiter unten in diesem Artikel beschrieben.
XML-Media-Type-Formatierer
XML-Formatierung wird von der XmlMediaTypeFormatter-Klasse bereitgestellt. Standardmäßig verwendet XmlMediaTypeFormatter die DataContractSerializer-Klasse zum Ausführen der Serialisierung.
Wenn Es Ihnen lieber ist, können Sie den XmlMediaTypeFormatter so konfigurieren, dass der XmlSerializer anstelle des DataContractSerializers verwendet wird. Legen Sie dazu die UseXmlSerializer-Eigenschaft auf "true" fest:
var xml = GlobalConfiguration.Configuration.Formatters.XmlFormatter;
xml.UseXmlSerializer = true;
Die XmlSerializer-Klasse unterstützt einen schmaleren Satz von Typen als DataContractSerializer, bietet jedoch mehr Kontrolle über das resultierende XML. Erwägen Sie die Verwendung von XmlSerializer , wenn Sie mit einem vorhandenen XML-Schema übereinstimmen müssen.
XML-Serialisierung
In diesem Abschnitt werden einige bestimmte Verhaltensweisen des XML-Formatierers mithilfe des Standardmäßigen DataContractSerializers beschrieben.
Standardmäßig verhält sich der DataContractSerializer wie folgt:
- Alle öffentlichen Lese-/Schreibeigenschaften und Felder werden serialisiert. Wenn Sie eine Eigenschaft oder ein Feld weglassen möchten, versehen Sie sie mit dem IgnoreDataMember-Attribut .
- Private und geschützte Member werden nicht serialisiert.
- Schreibgeschützte Eigenschaften werden nicht serialisiert. (Der Inhalt einer schreibgeschützten Auflistungseigenschaft wird jedoch serialisiert.)
- Klassen- und Membernamen werden in der XML genau so geschrieben, wie sie in der Klassendeklaration angezeigt werden.
- Es wird ein XML-Standardnamespace verwendet.
Wenn Sie mehr Kontrolle über die Serialisierung benötigen, können Sie die Klasse mit dem DataContract-Attribut versehen. Wenn dieses Attribut vorhanden ist, wird die Klasse wie folgt serialisiert:
- "Opt in"-Ansatz: Eigenschaften und Felder werden standardmäßig nicht serialisiert. Um eine Eigenschaft oder ein Feld zu serialisieren, versehen Sie sie mit dem DataMember-Attribut .
- Um ein privates oder geschütztes Element zu serialisieren, versehen Sie es mit dem DataMember-Attribut.
- Schreibgeschützte Eigenschaften werden nicht serialisiert.
- Um zu ändern, wie der Klassenname im XML-Code angezeigt wird, legen Sie den Parameter Name im DataContract-Attribut fest .
- Um zu ändern, wie ein Membername im XML-Code angezeigt wird, legen Sie den Parameter Name im DataMember-Attribut fest.
- Um den XML-Namespace zu ändern, legen Sie den Namespaceparameter in der DataContract-Klasse fest .
Read-Only-Eigenschaften
Schreibgeschützte Eigenschaften werden nicht serialisiert. Wenn eine schreibgeschützte Eigenschaft über ein unterstützendes privates Feld verfügt, können Sie das private Feld mit dem DataMember-Attribut markieren. Für diesen Ansatz ist das DataContract-Attribut für die Klasse erforderlich.
[DataContract]
public class Product
{
[DataMember]
private int pcode; // serialized
// Not serialized (read-only)
public int ProductCode { get { return pcode; } }
}
Datumsangaben
Datumsangaben werden im ISO 8601-Format geschrieben. Beispiel: "2012-05-23T20:21:37.9116538Z".
Einzug
Um eingerücktes XML zu schreiben, setzen Sie die Eigenschaft Indent auf true:
var xml = GlobalConfiguration.Configuration.Formatters.XmlFormatter;
xml.Indent = true;
Festlegen der XML-Serialisierer pro Typ
Sie können unterschiedliche XML-Serialisierer für verschiedene CLR-Typen festlegen. Beispielsweise verfügen Sie möglicherweise über ein bestimmtes Datenobjekt, für das XmlSerializer aus Gründen der Abwärtskompatibilität erforderlich ist. Sie können XmlSerializer für dieses Objekt verwenden und weiterhin DataContractSerializer für andere Typen verwenden.
Rufen Sie SetSerializer auf, um einen XML-Serialisierer für einen bestimmten Typ festzulegen.
var xml = GlobalConfiguration.Configuration.Formatters.XmlFormatter;
// Use XmlSerializer for instances of type "Product":
xml.SetSerializer<Product>(new XmlSerializer(typeof(Product)));
Sie können einen XmlSerializer oder ein beliebiges Objekt angeben, das von XmlObjectSerializer abgeleitet wird.
Entfernen des JSON- oder XML-Formatierers
Sie können den JSON-Formatierer oder den XML-Formatierer aus der Liste der Formatierer entfernen, wenn Sie sie nicht verwenden möchten. Die wichtigsten Gründe hierfür sind:
- Um die Web-API-Antworten auf einen bestimmten Medientyp zu beschränken. Sie können z. B. nur JSON-Antworten unterstützen und den XML-Formatierer entfernen.
- Um den Standardformatierer durch einen benutzerdefinierten Formatierer zu ersetzen. Beispielsweise könnten Sie den JSON-Formatierer durch Ihre eigene benutzerdefinierte Implementierung eines JSON-Formatierers ersetzen.
Der folgende Code zeigt, wie die Standardformatierer entfernt werden. Rufen Sie dies aus Ihrer Application_Start-Methode auf, die in "Global.asax" definiert ist.
void ConfigureApi(HttpConfiguration config)
{
// Remove the JSON formatter
config.Formatters.Remove(config.Formatters.JsonFormatter);
// or
// Remove the XML formatter
config.Formatters.Remove(config.Formatters.XmlFormatter);
}
Umgang mit kreisförmigen Objektverweisen
Standardmäßig schreiben die JSON- und XML-Formatierer alle Objekte als Werte. Wenn zwei Eigenschaften auf dasselbe Objekt verweisen oder dasselbe Objekt zweimal in einer Auflistung angezeigt wird, serialisiert der Formatierer das Objekt zweimal. Dies ist ein bestimmtes Problem, wenn Ihr Objektdiagramm Zyklen enthält, da der Serializer eine Ausnahme auslöst, wenn eine Schleife im Diagramm erkannt wird.
Berücksichtigen Sie die folgenden Objektmodelle und -controller.
public class Employee
{
public string Name { get; set; }
public Department Department { get; set; }
}
public class Department
{
public string Name { get; set; }
public Employee Manager { get; set; }
}
public class DepartmentsController : ApiController
{
public Department Get(int id)
{
Department sales = new Department() { Name = "Sales" };
Employee alice = new Employee() { Name = "Alice", Department = sales };
sales.Manager = alice;
return sales;
}
}
Wenn Sie diese Aktion aufrufen, löst der Formatierer eine Ausnahme aus, die in eine Statuscode 500 -Antwort (Interner Serverfehler) auf den Client übersetzt wird.
Um Objektverweise in JSON beizubehalten, fügen Sie den folgenden Code zur Application_Start Methode in der Datei "Global.asax" hinzu:
var json = GlobalConfiguration.Configuration.Formatters.JsonFormatter;
json.SerializerSettings.PreserveReferencesHandling =
Newtonsoft.Json.PreserveReferencesHandling.All;
Jetzt gibt die Controlleraktion JSON zurück, die wie folgt aussieht:
{"$id":"1","Name":"Sales","Manager":{"$id":"2","Name":"Alice","Department":{"$ref":"1"}}}
Beachten Sie, dass der Serialisierer beiden Objekten eine Eigenschaft "$id" hinzufügt. Außerdem wird erkannt, dass die Employee.Department-Eigenschaft eine Schleife erstellt, sodass der Wert durch einen Objektverweis ersetzt wird: {"$ref":"1"}.
Hinweis
Objektverweise sind in JSON nicht standard. Überlegen Sie vor der Verwendung dieses Features, ob Ihre Clients die Ergebnisse analysieren können. Es könnte besser sein, Zyklen aus dem Diagramm zu entfernen. Beispielsweise ist der Link von "Mitarbeiter" zurück zur Abteilung in diesem Beispiel nicht wirklich erforderlich.
Um Objektverweise in XML beizubehalten, haben Sie zwei Optionen. Die einfachere Option besteht darin, Ihrer Modellklasse hinzuzufügen [DataContract(IsReference=true)] . Der IsReference-Parameter aktiviert Objektverweise. Denken Sie daran, dass DataContract die Serialisierungsaktivierung aktiviert, daher müssen Sie den Eigenschaften auch DataMember-Attribute hinzufügen:
[DataContract(IsReference=true)]
public class Department
{
[DataMember]
public string Name { get; set; }
[DataMember]
public Employee Manager { get; set; }
}
Jetzt erzeugt der Formatierer XML ähnlich wie folgt:
<Department xmlns:i="http://www.w3.org/2001/XMLSchema-instance" z:Id="i1"
xmlns:z="http://schemas.microsoft.com/2003/10/Serialization/"
xmlns="http://schemas.datacontract.org/2004/07/Models">
<Manager>
<Department z:Ref="i1" />
<Name>Alice</Name>
</Manager>
<Name>Sales</Name>
</Department>
Wenn Sie Attribute für Ihre Modellklasse vermeiden möchten, gibt es eine weitere Option: Erstellen Sie eine neue typspezifische DataContractSerializer-Instanz , und legen Sie " preserveObjectReferences " im Konstruktor auf "true " fest. Legen Sie diese Instanz dann als serialisierer pro Typ im XML-Medientypformatierer fest. Der folgende Code zeigt, wie dies funktioniert:
var xml = GlobalConfiguration.Configuration.Formatters.XmlFormatter;
var dcs = new DataContractSerializer(typeof(Department), null, int.MaxValue,
false, /* preserveObjectReferences: */ true, null);
xml.SetSerializer<Department>(dcs);
Testen der Objekt serialisierung
Wenn Sie Ihre Web-API entwerfen, ist es hilfreich, zu testen, wie Ihre Datenobjekte serialisiert werden. Dazu müssen Sie keinen Controller erstellen oder eine Controller-Aktion aufrufen.
string Serialize<T>(MediaTypeFormatter formatter, T value)
{
// Create a dummy HTTP Content.
Stream stream = new MemoryStream();
var content = new StreamContent(stream);
/// Serialize the object.
formatter.WriteToStreamAsync(typeof(T), value, stream, content, null).Wait();
// Read the serialized string.
stream.Position = 0;
return content.ReadAsStringAsync().Result;
}
T Deserialize<T>(MediaTypeFormatter formatter, string str) where T : class
{
// Write the serialized string to a memory stream.
Stream stream = new MemoryStream();
StreamWriter writer = new StreamWriter(stream);
writer.Write(str);
writer.Flush();
stream.Position = 0;
// Deserialize to an object of type T
return formatter.ReadFromStreamAsync(typeof(T), stream, null, null).Result as T;
}
// Example of use
void TestSerialization()
{
var value = new Person() { Name = "Alice", Age = 23 };
var xml = new XmlMediaTypeFormatter();
string str = Serialize(xml, value);
var json = new JsonMediaTypeFormatter();
str = Serialize(json, value);
// Round trip
Person person2 = Deserialize<Person>(json, str);
}