ASP.NET Web API 'sinde JSON ve XML serileştirme

, Mike te son

Bu makalede ASP.NET Web API 'sindeki JSON ve XML formatlayıcıları açıklanmaktadır.

ASP.NET Web API 'sinde, bir medya türü biçimlendiricisi şunları yapabilir:

  • Bir HTTP ileti gövdesinden CLR nesnelerini okuma
  • CLR nesnelerini bir HTTP ileti gövdesine yazma

Web API 'si hem JSON hem de XML için medya türü biçimleri sağlar. Framework, varsayılan olarak bu biçimleri ardışık düzene ekler. İstemciler, HTTP isteğinin Accept üst bilgisinde JSON veya XML isteğinde bulunabilir.

İçindekiler

JSON Media-Type biçimlendiricisi

JSON biçimlendirmesi, Jsonmediatypeformatter sınıfı tarafından sağlanır. Varsayılan olarak, Jsonmediatypeformatter serileştirme gerçekleştirmek için JSON.net kitaplığını kullanır. Json.NET, üçüncü taraf bir açık kaynak projem.

İsterseniz, Json.NET yerine DataContractJsonSerializer kullanmak Için Jsonmediatypeformatter sınıfını yapılandırabilirsiniz. Bunu yapmak için Usedatacontractjsonserializer özelliğini trueolarak ayarlayın:

var json = GlobalConfiguration.Configuration.Formatters.JsonFormatter;
json.UseDataContractJsonSerializer = true;

JSON Seri Hale Getirme

Bu bölümde, varsayılan JSON.net serileştirici kullanılarak JSON biçimlendirici 'nin bazı belirli davranışları açıklanmaktadır. Bu, Json.NET kitaplığı hakkında kapsamlı bir belge olması anlamına gelir; daha fazla bilgi için JSON.net belgelerinebakın.

Ne seri hale getirilebilir?

Varsayılan olarak, tüm ortak özellikler ve alanlar serileştirilmiş JSON 'a dahil edilir. Bir özellik veya alanı atlamak için, bunu Jsonıgnore özniteliğiyle süsle.

public class Product
{
    public string Name { get; set; }
    public decimal Price { get; set; }
    [JsonIgnore]
    public int ProductCode { get; set; } // omitted
}

Bir " katılım yaklaşımı tercih ediyorsanız " , sınıfı DataContract özniteliğiyle süsler. Bu öznitelik mevcutsa, DataMemberöğesine sahip olmadıkları müddetçe Üyeler göz ardı edilir. Özel üyeleri seri hale getirmek için DataMember de kullanabilirsiniz.

[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 özellikleri

Salt okuma özellikleri varsayılan olarak serileştirilir.

Tarihler

Varsayılan olarak, Json.NET tarihleri ıso 8601 biçiminde yazar. UTC (Eşgüdümlü Evrensel Saat) tarihleri "Z" sonekiyle yazılır. Yerel saat içindeki tarihler, saat dilimi konumunu içerir. Örneğin:

2012-07-27T18:51:45.53403Z         // UTC
2012-07-27T11:51:45.53403-07:00    // Local

Varsayılan olarak, Json.NET saat dilimini korur. DateTimeZoneHandling özelliğini ayarlayarak bunu geçersiz kılabilirsiniz:

// Convert all dates to UTC
var json = GlobalConfiguration.Configuration.Formatters.JsonFormatter;
json.SerializerSettings.DateTimeZoneHandling = Newtonsoft.Json.DateTimeZoneHandling.Utc;

ISO 8601 yerine MICROSOFT JSON tarih biçimini () kullanmayı tercih ediyorsanız "\/Date(ticks)\/" , serileştirici ayarlarındaki DateFormatHandling özelliğini ayarlayın:

var json = GlobalConfiguration.Configuration.Formatters.JsonFormatter;
json.SerializerSettings.DateFormatHandling 
= Newtonsoft.Json.DateFormatHandling.MicrosoftDateFormat;

Girintileme

Girintili JSON yazmak için biçimlendirme ayarını biçimlendirme. girintiliolarak ayarlayın:

var json = GlobalConfiguration.Configuration.Formatters.JsonFormatter;
json.SerializerSettings.Formatting = Newtonsoft.Json.Formatting.Indented;

Kamel büyük harfleri

Veri modelinizi değiştirmeden JSON özellik adlarını kamel büyük küçük harfe yazmak için, seri hale getirici üzerinde Camelcasepropertynamescontractresolver ' ı ayarlayın:

var json = GlobalConfiguration.Configuration.Formatters.JsonFormatter;
json.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver();

Anonim ve Weakly-Typed nesneler

Bir eylem yöntemi, anonim bir nesne döndürebilir ve bunu JSON 'a seri hale getirebilirsiniz. Örneğin:

public object Get()
{
    return new { 
        Name = "Alice", 
        Age = 23, 
        Pets = new List<string> { "Fido", "Polly", "Spot" } 
    };
}

Yanıt iletisi gövdesinde şu JSON yer alacak:

{"Name":"Alice","Age":23,"Pets":["Fido","Polly","Spot"]}

Web API 'niz istemcilerden gevşek olarak yapılandırılmış JSON nesneleri alırsa, istek gövdesinin serisini ' de birNewtonsoft.Jsiçin silebilirsiniz ** . LINQ. JObject** türü.

public void Post(JObject person)
{
    string name = person["Name"].ToString();
    int age = person["Age"].ToObject<int>();
}

Ancak, türü kesin belirlenmiş veri nesneleri kullanmak genellikle daha iyidir. Daha sonra verileri kendiniz ayrıştırmanıza gerek kalmaz ve model doğrulamasının avantajlarını elde edersiniz.

XML serileştiricisi anonim türleri veya JObject örneklerini desteklemez. JSON verileriniz için bu özellikleri kullanırsanız, bu makalenin ilerleyen kısımlarında açıklandığı gibi, XML biçimlendirici ' i ardışık düzen öğesinden kaldırmanız gerekir.

XML Media-Type biçimlendiricisi

XML biçimlendirmesi Xmlmediatypeformatter sınıfı tarafından sağlanır. Varsayılan olarak, Xmlmediatypeformatter serileştirme işlemini gerçekleştirmek için DataContractSerializer sınıfını kullanır.

Tercih ederseniz, XmlmediatypeformatterDataContractSerializeryerine XmlSerializer 'ı kullanacak şekilde yapılandırabilirsiniz. Bunu yapmak için useXmlSerializer özelliğini trueolarak ayarlayın:

var xml = GlobalConfiguration.Configuration.Formatters.XmlFormatter;
xml.UseXmlSerializer = true;

XmlSerializer sınıfı, DataContractSerializer'dan daha dar bir tür kümesini destekler, ancak sonuçta elde edilen XML üzerinde daha fazla denetim sağlar. Mevcut bir XML şemasıyla eşleşmesi gerekiyorsa XmlSerializer kullanmayı düşünün.

XML seri hale getirme

Bu bölümde, varsayılan DataContractSerializerkullanılarak XML biçimlendirici 'nin bazı belirli davranışları açıklanmaktadır.

Varsayılan olarak, DataContractSerializer aşağıdaki gibi davranır:

  • Tüm genel okuma/yazma özellikleri ve alanları serileştirilir. Bir özellik veya alanı atlamak için, ıgnoredatamember özniteliğiyle süslenmiş.
  • Özel ve korunan Üyeler serileştirilmez.
  • Salt okuma özellikleri serileştirilmez. (Ancak, salt okunurdur bir koleksiyon özelliğinin içeriği serileştirilir.)
  • Sınıf ve üye adları, sınıf bildiriminde göründükleri gibi XML 'e tam olarak yazılır.
  • Varsayılan bir XML ad alanı kullanılır.

Serileştirme üzerinde daha fazla denetime ihtiyacınız varsa, bir sınıfı DataContract özniteliğiyle süslemek için kullanabilirsiniz. Bu öznitelik mevcut olduğunda, sınıf aşağıdaki şekilde serileştirilir:

  • "Kabul etme " yaklaşımı: Özellikler ve alanlar varsayılan olarak serileştirilmez. Bir özellik veya alanı seri hale getirmek için, bunu DataMember özniteliğiyle süsle.
  • Özel veya korumalı bir üyeyi seri hale getirmek için, bunu DataMember özniteliğiyle süsle.
  • Salt okuma özellikleri serileştirilmez.
  • Sınıf adının XML 'de nasıl göründüğünü değiştirmek için, DataContract özniteliğinde Name parametresini ayarlayın.
  • Bir üye adının XML 'de nasıl göründüğünü değiştirmek için, DataMember özniteliğinde Name parametresini ayarlayın.
  • XML ad alanını değiştirmek için, DataContract sınıfında ad alanı parametresini ayarlayın.

Read-Only özellikleri

Salt okuma özellikleri serileştirilmez. Salt okunurdur bir özelliğin bir yedekleme özel alanı varsa, özel alanı DataMember özniteliğiyle işaretleyebilirsiniz. Bu yaklaşım, sınıfında DataContract özniteliğini gerektirir.

[DataContract]
public class Product
{
    [DataMember]
    private int pcode;  // serialized

    // Not serialized (read-only)
    public int ProductCode { get { return pcode; } }
}

Tarihler

Tarihler ISO 8601 biçiminde yazılır. Örneğin, " 2012-05-23T20:21:37.9116538 z " .

Girintileme

Girintili XML yazmak için Girintile özelliğini trueolarak ayarlayın:

var xml = GlobalConfiguration.Configuration.Formatters.XmlFormatter;
xml.Indent = true;

Per-Type XML serileştiricileri ayarlama

Farklı CLR türleri için farklı XML serileştiricileri ayarlayabilirsiniz. Örneğin, geriye doğru uyumluluk için XmlSerializer gerektiren belirli bir veri nesneniz olabilir. Bu nesne için XmlSerializer 'ı kullanabilir ve diğer türler için DataContractSerializer kullanmaya devam edebilirsiniz.

Belirli bir tür için bir XML serileştirici ayarlamak için setserializerçağırın.

var xml = GlobalConfiguration.Configuration.Formatters.XmlFormatter;
// Use XmlSerializer for instances of type "Product":
xml.SetSerializer<Product>(new XmlSerializer(typeof(Product)));

Bir XmlSerializer veya XmlObjectSerializeröğesinden türetilen herhangi bir nesne belirtebilirsiniz.

JSON veya XML biçimlendirici kaldırılıyor

JSON biçimlendirici veya XML biçimlendirici, bunları kullanmak istemiyorsanız Formatters listesinden kaldırabilirsiniz. Bunu yapmak için başlıca nedenler şunlardır:

  • Web API yanıtlarınızı belirli bir medya türüyle sınırlamak için. Örneğin, yalnızca JSON yanıtlarını desteklemeye karar verebilir ve XML biçimlendirici ' ı kaldırabilirsiniz.
  • Varsayılan biçimlendiricisi özel bir biçimlendirici ile değiştirmek için. Örneğin, JSON biçimlendirici bir JSON biçimlendiricisi kendi özel uygulamanıza göre değiştirebilirsiniz.

Aşağıdaki kod, varsayılan formatlamalara nasıl kaldırılacağını gösterir. Bunu, Global. asax içinde tanımlanan uygulama _ başlatma yönteminden çağırın.

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

Döngüsel nesne başvurularını işleme

Varsayılan olarak, JSON ve XML formatlayıcıları tüm nesneleri değer olarak yazar. İki özellik aynı nesneye başvurursanız veya aynı nesne bir koleksiyonda iki kez görünürse, biçimlendirici nesneyi iki kez serileştirilir. Bu, nesne grafikleriniz döngüler içeriyorsa, bu bir sorundur çünkü seri hale getirici grafikte bir döngü algıladığında bir özel durum oluşturur.

Aşağıdaki nesne modellerini ve denetleyiciyi göz önünde bulundurun.

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

Bu eylemi çağırmak, biçimlendirici bir durum kodu 500 (Iç sunucu hatası) yanıtına çeviren bir özel durum oluşturulmasına neden olur.

JSON 'daki nesne başvurularını korumak için, Global. asax dosyasındaki uygulama _ başlatma yöntemine aşağıdaki kodu ekleyin:

var json = GlobalConfiguration.Configuration.Formatters.JsonFormatter;
json.SerializerSettings.PreserveReferencesHandling = 
    Newtonsoft.Json.PreserveReferencesHandling.All;

Artık denetleyici eylemi şuna benzer bir JSON döndürür:

{"$id":"1","Name":"Sales","Manager":{"$id":"2","Name":"Alice","Department":{"$ref":"1"}}}

Seri hale getiricinin " her iki nesneye bir $id özelliği eklediğine dikkat edin " . Ayrıca, Employee. Department özelliğinin bir döngü oluşturduğunu algılar, bu yüzden değeri bir nesne başvurusuyla değiştirir: { " $ref " : " 1 " }.

Note

Nesne başvuruları JSON 'da standart değildir. Bu özelliği kullanmadan önce, istemcilerinizin sonuçları ayrıştırabilecek olup olmayacağını göz önünde bulundurun. Yalnızca grafikten döngüleri kaldırmak daha iyi olabilir. Örneğin, çalışana kadar olan bağlantı bu örnekte gerçekten gerekli değildir.

XML 'deki nesne başvurularını korumak için iki seçeneğiniz vardır. Daha basit seçeneği [DataContract(IsReference=true)] model sınıfınıza eklemektir. IsReference parametresi nesne başvurularını mümkün bir şekilde sunar. DataContract serileştirme kabul eder, bu nedenle ayrıca özelliklere DataMember öznitelikleri eklemeniz gerekir:

[DataContract(IsReference=true)]
public class Department
{
    [DataMember]
    public string Name { get; set; }
    [DataMember]
    public Employee Manager { get; set; }
}

Artık biçimlendirici şuna benzer bir XML oluşturacak:

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

Model sınıfınıza ait özniteliklerin önüne geçmek istiyorsanız, başka bir seçenek vardır: yeni türe özgü bir DataContractSerializer örneği oluşturun ve bu tür Için preserveObjectReferences öğesini true olarak ayarlayın. Sonra bu örneği, XML medya türü biçimlendirici üzerinde tür başına seri hale getirici olarak ayarlayın. Aşağıdaki kod bunun nasıl yapılacağını göstermektedir:

var xml = GlobalConfiguration.Configuration.Formatters.XmlFormatter;
var dcs = new DataContractSerializer(typeof(Department), null, int.MaxValue, 
    false, /* preserveObjectReferences: */ true, null);
xml.SetSerializer<Department>(dcs);

Test nesnesi serileştirme

Web API 'nizi tasarlarken, veri nesnelerinizin nasıl seri hale getirileceğinin test olması yararlı olur. Bunu, denetleyici oluşturmadan veya bir denetleyici eylemi çağırmadan yapabilirsiniz.

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