Serileştirme (Serialization)
.NET uygulamasında bir nesne yarattığımızda, verinin bellekte nasıl tutulduğunu geliştirici olarak düşünmeyiz. Bu işle, .NET framework bizim adımıza ilgilenir.
Eğer bir nesnenin içeriğini bir dosyaya yazdırmak istediğimizde nesnenin nasıl temsil edildiği hakkında düşünmemiz gerekecektir. Çünkü bu nesneyi farklı bir forma dönüştürmemiz gerekir. Bu dönüşüme Serileştirme(“Serialization”) denilmektedir.
Serileştirme(Serialization) Nedir? Serialization, “System.Runtime.Serialization” isim alanı tarafından implement edilmektedir. Bu işlem, nesnelerin saklanıp veya transfer edilip tekrar oluşturulabilmesi için serileştirilmesi ve geri dönüşüm (deserialization) işlemlerini kapsar.
Serileştirme, bir nesnenin saklanması veya transfer edilebilmesi için doğrusal byte dizilerine dönüştürülmesidir. Geri serileştirme (DeSerialization) ise serileştirilmiş olan byte dizisinin nesneye dönüştürülmesidir.
using System; using System.IO; using System.Runtime.Serialization.Formatters.Binary; namespace ConsoleApplication3 { class Program { static void Main(string[] args) string data = "Merhaba Bilgisayar Mühendisliği"; FileStream fs = new FileStream("Serialized.Data", FileMode.Create); BinaryFormatter bf = new BinaryFormatter(); bf.Serialize(fs, data); fs.Close(); }
Sadece tek bir stringin dosyada tutulmasına ihtiyacımız varsa serileştirmeyi kullanmak zorunda değiliz. Bu stringi bir text dosyasına da doğrudan saklamak mümkündür. Serileştirmenin önemi, özellikle daha karmaşık bilgilerin(Zaman,tarih vs.) saklanmasında ortaya çıkmaktadır.
using System; using System.IO; using System.Runtime.Serialization.Formatters.Binary; namespace ConsoleApplication3 { class Program { static void Main(string[] args) FileStream fs = new FileStream("SerializedString.Date", FileMode.Create); BinaryFormatter bf = new BinaryFormatter(); bf.Serialize(fs, System.DateTime.Now); fs.Close(); }
Deserialization Deserialization işlemi, serileştirilmiş nesnenin bilgilerini kullanarak yeniden nesnenin oluşturulmasını sağlar.
using System; using System.IO; using System.Runtime.Serialization.Formatters.Binary; namespace ConsoleApplication3 { class Program { static void Main(string[] args) string data = ""; FileStream fs = new FileStream("SerializedString.Data", FileMode.Open); BinaryFormatter bf = new BinaryFormatter(); data = (string)bf.Deserialize(fs); Console.WriteLine(data); fs.Close(); }
using System; using System.IO; using System.Runtime.Serialization.Formatters.Binary; namespace ConsoleApplication3 { class Program { static void Main(string[] args) { DateTime previousTime = new DateTime(); FileStream fs = new FileStream("SerializedDate.Data", FileMode.Open); BinaryFormatter bf = new BinaryFormatter(); previousTime = (DateTime)bf.Deserialize(fs); Console.WriteLine("Day:"+previousTime.TimeOfDay.ToString()); fs.Close(); }
Sınıflar Nasıl Serileştirilir? Bu özellik sayesinde, başka bir geliştirici bu sınıfların örneklerini (instances) kolaylıkla saklayabilir ve transfer edebilir. Sınıflar serileştirildiğinde, sınıfların tüm üyeleri (private olanlar dahil olmak üzere) serileşirler.
Sınıfların bazı üyelerinin serileştirme sırasında saklanmasına gerek yoktur. (Bazı üyeler hesaplanabilen/geçici değerler) taşıyabilirler.
[Serializable] class ShoppingCartItem { public int productId; public decimal price; public int quantity; public decimal total; public ShoppingCartItem(int _productID,decimal _price, int _quantity) productId=_productID; price=_price; quantity = _quantity; total = price * quantity; }
[Serializable] class ShoppingCartItem { public int productId; public decimal price; public int quantity; [NonSerialized] public decimal total; public ShoppingCartItem(int _productID,decimal _price, int _quantity) productId=_productID; price=_price; quantity = _quantity; total = price * quantity; }
Artık, nesne serileştiğinde, total üyesi gözardı edilip bu işleme dahil edilmeyecektir. Fakat, total değişkeninin değeri, nesne deserialize edilip kullanılmadan önce hesaplanmaktadır.
Serileştirilmeyen üyelerin otomatik olarak başlangıç değerlerinin verilmesi için, IDeserializationCallback arayüzü kullanılması ve bunun implement edilmesi gerekir. Sınıf her defasında deserialize edildiğinde, çalışma zamanında IDeserializationCallBack.OnDeserialization metodu, deserialization bittikten sonra çağrılır.
[Serializable] class ShoppingCartItem { public int productId; public decimal price; public int quantity; [NonSerialized] public decimal total; public ShoppingCartItem(int _productID,decimal _price, int _quantity) productId=_productID; price=_price; quantity = _quantity; total = price * quantity; } ./..
//Deserialization işleminden sonra, total'in değerini hesapla [Serializable] class ShoppingCartItem { ... ... public void OnDeserialization(object sender) //Deserialization işleminden sonra, total'in değerini hesapla total = price * quantity; }
Versiyon Uyumluluğu Programın daha önceki sürümü tarafından serileştirilmiş olan bir nesneyi daha sonraki bir sürümünde deserialize etmeye çalışıldığında uyumluluk sorunları yaşamak mümkündür.
Örneğin, kendi sınıfımıza yeni bir üye eklediğimizde ve ardından bu öğeye sahip olmayan bir öğeyi deserialize etmeye çalıştığımızda, çalışma zamanında exception oluşur.
Bu sorunu çözmek için 2 seçenek vardır: Özel serileştirmeyi kullanmak. (Custom Serialization) [OptionalField] niteliğini, yeni eklenen üyelerde kullanmak.
[OptionalField] niteliği, serileştirme işlemine etki etmez [OptionalField] niteliği, serileştirme işlemine etki etmez. Sadece, deserialization sırasında, eğer üye önceden serileştirilmemişse, bu üyenin değerini null olarak atar ve böylece exception oluşturmaz.
[Serializable] class ShoppingCartItem { public int productId; public decimal price; public int quantity; [NonSerialized] public decimal total; [OptionalField] public bool texable; public ShoppingCartItem(int _productID,decimal _price, int _quantity) productId=_productID; price=_price; quantity = _quantity; total = price * quantity; }
Versiyon uyumluluğunu sağlamak için; Serileştirilmiş bir alanı(field) sınıftan çıkartılmamalıdır. [NonSerialized] niteliğini, daha önceki versiyonda [NonSerialized] olarak belirtilmemiş bir alana uygulayın. Serileştirilmiş bir alanın türünü ve ismini değiştirmeyin.
Versiyon uyumluluğunu sağlamak için; Serileştirilen sınıfa yeni bir alan eklenecekse [OptionalField] niteliği uygulanmalıdır. Bütün seçimlik alanlar için, anlamlı başlangıç değerleri oluşturulmalıdır.
Serileştirme Biçimi (Serialization Format) .NET Framework System.Runtime.Serialization namespace’inde, serileştirilecek veriyi biçimlendirmek için 2 sınıf içerir: BinaryFormatter SoapFormatter Bu sınıflardan ikisi de IRemotingFormatter arayüzünü implement eder.
BinaryFormatter System.Runtime.Serialization.Formatters.Binary namespace’inde bulunur. Bu sınıf kullanılarak serileştirilen nesneler, sadece .NET Framework tabanlı uygulamalar tarafından okunabilirler.
SoapFormatter System.Runtime.Serialization.Formatters.Soap namespace’inde bulunur. Bu biçimlendirici XML tabanlı olup, nesnelerin ağ üzerinde serileştirilerek gönderilmesinde ve .NET Framework tabanlı olmayan uygulamalar tarafından da okunabilmesine olanak tanır.
SoapFormatter’ı kullanabilmek için System. Runtime. Serialization SoapFormatter’ı kullanabilmek için System.Runtime.Serialization.Formatters.Soap.dll’e referans vermek gerekir. BinaryFormatter’ın aksine varsayılı olarak referans edilmiş bir şekilde gelmez. Her iki biçimlendiricinin yazım şekli birbirine benzese de serileştirilen verinin şekli değişiktir.
SoapFormatter, verimli olmamasına rağmen, farklı platformlar arasında verilerin transfer edilebilmesini sağlar.
XML Serileştirme (XML Serialization)
XML, standardize edilmiş, text tabanlı belge formatıdır XML, standardize edilmiş, text tabanlı belge formatıdır. Uygulamaları okunabilir bir şekilde depolamak amacıyla kullanılır. Her tür verinin saklanması için kullanılabilir (Dölüman, resim, müzik, binary dosya, veritabanı bilgiler vb.)
System.Xml.Serialization namespace’i nesneleri XML dosyalarına (veya dosylarından) yazıp okuyabilmek için metotlar barındırır. Bu şekilde serileştirilmiş olan nesneler farklı bilgisayarlar arasında Web Servisleri kullanılarak transfer edilebilirler. Böyle bir durumda uzaktaki bilgisayarın .NET Framework’ü kullanmasına gerek yoktur.
Avantajları XML serileştirmenin standart serileştirmeye göre bazı üstünlükleri vardır: Daha Fazla Birlikte Kullanılabilirlik Daha Fazla Yönetici Dostu İleriye Yönelik Uyumluluk
Dezavantajları Sadece public dataları serileştirebilir. Nesne graflarını serileştirmek mümkün değildir. Sadece nesneler üzerinde uygulanabilir.
FileStream fs = new FileStream("SerializedDate.XML", FileMode.Create); using System; using System.IO; using System.Xml.Serialization; namespace ConsoleApplication3 { class Program { static void Main(string[] args) FileStream fs = new FileStream("SerializedDate.XML", FileMode.Create); XmlSerializer xs = new XmlSerializer(typeof(DateTime)); xs.Serialize(fs, DateTime.Now); fs.Close(); }
FileStream fs = new FileStream("SerializedDate.XML", FileMode.Open); using System; using System.IO; using System.Xml.Serialization; namespace ConsoleApplication3 { class Program { static void Main(string[] args) { FileStream fs = new FileStream("SerializedDate.XML", FileMode.Open); XmlSerializer xs = new XmlSerializer(typeof(DateTime)); DateTime previousTime = (DateTime)xs.Deserialize(fs); fs.Close(); Console.WriteLine("Time:"+previousTime.TimeOfDay.ToString()); } }
Sınıflar XML Serializer ile Nasıl Serileştirilir? XML serileştirme kullanarak serileştirilecek bir sınıf yaratmak için; Sınıf, public olarak belirtilmelidir. Serileştirilecek olan tüm üyeler public olarak belirtilmelidir. Parametre almayan bir yapıcı oluşturulmalıdır.
XML serileştirmesi için gerekli olan yeterlilikleri sağlayan bir sınıf, XML niteliklerinin hiçbirine sahip değildir. XML elemanlarının isimleri sınıf ve üye isimlerine bağlıdır. Her üye ayrı bir XML elemanı olacak şekilde serileştirilir.
Kaynak Chapter 5 – Pages 169-192