Değerli Okurlarım Merhabalar,
Serileştirme(Serialization) ve
çözümleme(Encoding) çoğu zaman bir birlerine karıştırılan
kavramlar olabilmektedir. Oysaki
aralarında çok ince ama bir o kadarda önemli farklılıklar vardır.
Serileştirme ve çözümleme, SOA(Service Oritented Architecture)
tarzındaki uygulama çözümlerinde sıklıkla kullanılmaktadır. Nitekim bu
tip mimarilerde servis ve istemci arasında yapılan veri transferlerinde
bilginin serileştirilmesi ve mesajların çözümlenmesi gerekmektedir.
Serileştirme özet olarak nesne grafiğinin(Object Graph) byte dizisine
dönüştürülmesi olarak düşünülebilir. Bu sayede çalışma zamanı(Run-Time) nesne örneklerinin
herhangibir kaynakta sürekli olarak saklanması mümkün olabilmektedir.
Bir başka deyişle nesneye ait veri içeriğinin, bir dosyada,
veritabanında, bellekte tutulması mümkündür. Ancak en önemli olan kısmı
söz konusu nesne içeriğinin başka bir ortama herhangibir protokol
üzerinden taşınabiliyor olmasıdır ki bu dağıtık uygulama(Distributed
Applications) çözümlerinde
kilit rollerden birisidir.
Peki duruma WCF(Windows Communication
Foundation) açısından bakıldığında göze çarpan noktalar nelerdir? WCF tarafında
serileştirme sadece nesne grafiğinin bir byte dizisine dönüştürülmesi
olarak yorumlanmaz. Bunun yerine nesne grafiğinin bir XML InfoSet(XML
Information Set) içeriğine dönüştürülmesi olarak yorumlanır. XML
InfoSet
içeriği WCF mesajlarının oluşturulmasında kullanılmaktadır. XML InfoSet,
XML' in bir üst kümesi olarak düşünülebilir. XML InfoSet sayesinde, XML
içeriğinin sadece Text formatında olma zorunluluğu ortadan kalkmaktadır.
Bir başka deyişle nesne içeriğinin, örneğin binary XML formatında
üretilmesi mümkün olabilmektedir. Bu WCF açısından önemlidir çünkü
interoperability, performans gibi konularda XML formatının
Text bazlı
olmayan versiyonlarının kullanılmasının avantajlarından
yararlanılabilinir.
 |
XML InfoSet, WCF mimarisine özgü
bir kavram değildir. Nitekim Asp.Net Web Service modelindeki
uygulamalarda XML InfoSet yaklaşımını kullanmaktadır. |
WCF mimarisinde kullanılmakta olan
serileştirici tipler aşağıdaki gibidir.

Çözümleme(Encoding) kısaca, WCF
mesajlarının byte dizisi haline dönüştürülmesi olarak ele alınabilir. Bu
sayede mesajın içeriğinin iletişim kanalları(Transport Channels) üzerinden aktarılabilmesi
mümkün olmaktadır. WCF mimarisi temel olarak beş farklı çözümleme
formatını(Encoding Formats) desteklemektedir.

WCF tarafında duruma göre yukarıda
bahsedilen formatları ele alan hazır encoder tipleri bulunmaktadır.
Özellikle .Net uygulamaları arasında bir mesajlaşma söz konusu ise
performans adına BinaryMessageEncoder, platformlar arası uyumluluk
(Interoperability) gerekiyorsa TextMessageEncoder veya MtomMessageEncoder,
Ajax tabanlı web istemcilerinin yer aldığı
senaryolarda ise, JsonMessageEncoder tipleri devreye girmektedir. Önemli olan
noktalardan biriside WCF tarafında encoding sisteminin
genişletilebilmesidir. Yani yeni çıkan çözümleme formatlarına uygun
eklemeler ve ilaveler yapılabilir.
Serileştirme ve çözümleme WCF tarafında
bir arada düşünülmesi gereken konulardır. Nitekim, nesne içeriğinin
serileştirilerek XML InfoSet haline getirilmesi sadece sürecin ilk
adımıdır. Sonrasında bu InfoSet bilgisinden yararlanılarak karşı tarafa
gönderilecek olan mesajın üretilmesi için bir çözümleme(Encoding) işlemi
yapılır. Bu sürecin sonunda oluşturulan mesaj, iletişim kanalları
üzerinden karşı tarafa gönderilir. Burada hatırlanması gereken
noktalardan biriside, encoding işlemlerinde çalışma zamanında oluşan
kanal yığının(Channel Stack) içeriğidir. Bilindiği gibi bu yığınında
mutlaka Encoding Channel ve Transport Channel kanallarının olması
gerekir. İşte Encoding Channel içerisinde, serileştirilerek
InfoSet
haline gelen bilginin ilgili Encoder tipine göre çözümlenerek mesaj
içeriği haline getirilmesi sağlanmaktadır.
İlk olarak WCF serileştirme
opsiyonlarına bakmakta yarar bulunmaktadır. WCF, varsayılan olarak
DataContractSerializer tipini baz alarak serileştirme işlemlerini
gerçekleştirmektedir. DataContractSerializer, nesneyi serileştirirken
XSD şemalarını kullanır. Bir başka deyişle söz konusu CLR(Common
Language Runtime) tipini, karşılığı olan XSD tipi ile ifade eder. Bu
sayede farklı platformların serileştirilen nesneyi kullanabilme imkanı
doğar. Örneğin .Net tarafındaki System.String tipi XSD
tarafında
xs:String olarak ele alınır. Bu tipin kullanıldığı veri
sözleşmesini(Data Contract) kullanacak olan bir java uygulamasıda java.lang.String tipini karşılık olarak ele alır. Bir başka deyişle
XSD
formatı ilkel veri tiplerini baz alaraktan bir köprü vazifesini görür.
Elbette karmaşık tiplerde(Complex Type) ilkel tip seviyelerine
indirilerekten serileştirme işlemine tabi tutulurlar. Tabiki burada göz
ardı edilmemesi gereken önemli bir nokta vardır. Özellikle karmaşık
tiplerde DataContract ve DataMember nitelikleri(attributes)
ile
serileştirme işlemi sağlanmaktadır. (İlerleyen örneklerde aşağıdaki yer
alan Urun isimli tip kullanılmaktadır.)

using System;
using
System.Runtime.Serialization;
namespace Formatters
{
[DataContract(Namespace="http://www.bsenyurt.com/Urun")]
class Urun
{
private int _id;
private string _ad;
private double _listeFiyati;
private DateTime _stokTarihi;
public Urun(int id, string ad, double
listeFiyati, DateTime stokTarihi)
{
Id = id;
Ad = ad;
ListeFiyati =
listeFiyati;
StokTarihi =
stokTarihi;
}
[DataMember]
public DateTime StokTarihi
{
get { return
_stokTarihi; }
set {
_stokTarihi = value; }
}
[DataMember]
public double ListeFiyati
{
get { return
_listeFiyati; }
set {
_listeFiyati = value; }
}
[DataMember]
public string Ad
{
get { return
_ad; }
set { _ad =
value; }
}
[DataMember]
public int Id
{
get { return
_id; }
set { _id =
value; }
}
}
} |
DataContractSerializer ile
Serileştirme
DataContractSerializer tipi .Net
Framework 3.0 ile birlikte gelen ve WCF tarafında varsayılan olarak
kullanılan bir serileştiricidir. Yukarıda yer alan Urun sınıfına ait
nesne örneğini serileştirmek amacıyla DataContractSerializer tipi, örnek
bir Console uygulamasına ait aşağıdaki kod parçası ile ele alınabilir.(Söz
konusu Console uygulaması .Net Framework 3.5 şablonunda
geliştirilmiştir.)
using System;
using System.IO;
using System.Xml.Schema;
using
System.Runtime.Serialization;
namespace Formatters
{
class Program
{
static void Main(string[] args)
{
#region
DataContractSerilazier ile Type Serileştirme
//
Serileştirilecek nesne örneği.
Urun
mouse=new Urun(1,"Mx Mouse Optic",10,new DateTime(2004,1,1));
//
Serileştirici sınıf örneklenir.
// Parametre
olarak serileştirilecek veri sözleşmesi tipi verilir.
DataContractSerializer dcSerializer = new
DataContractSerializer(typeof(Urun));
//
Serileştirme sonuçlarının yazılacağı örnek bir Stream
oluşturulur.
FileStream stream2
= new FileStream("Urun.xml", FileMode.Create, FileAccess.Write);
//
WriteObject metodu ile stream2 nesnesi ile tanımlanan Stream
üzerine mouse isimli Urun nesne örneği verisi aktarılır
dcSerializer.WriteObject(stream2, mouse);
stream2.Close(); // Stream kapatılır.
#endregion
#region
DataContractSerializer ile Type Ters-Serileştirme
// Urun.xml
dosyası var ise ters serileştirme işlemleri yapılır.
if
(File.Exists("Urun.xml"))
{
// DataContractSerializer nesnesi örneklenir.
DataContractSerializer dcDeSerializer = new
DataContractSerializer(typeof(Urun));
// Serileştirilmiş veri içeriğinin bulunduğu dosyayı okumak için
bir Stream örneklenir.
FileStream stream5 = new FileStream("Urun.xml", FileMode.Open,
FileAccess.Read);
// ReadObject metodu ile parametre olarak verilen Stream
içerisindeki bilgi ters serileştirme işlemine tabi tutulur.
// Dönüş türü object olduğu için sonucun uygun türe cast
edilmesi gereklidir.
Urun
urn=(Urun)dcDeSerializer.ReadObject(stream5);
// Elde edilen nesne örneği bilgisi ekrana yazdırılır.
Console.WriteLine("Id :{0} Ad:{1} Fiyat:{2} Stok Tarihi:{3}",
urn.Id.ToString(), urn.Ad, urn.ListeFiyati.ToString("C2"),
urn.StokTarihi.ToString());
// Stream kapatılır.
stream5.Close();
}
#endregion
#region
DataContractSerializer ile Array Serileştirme
Urun[] urunler ={
new Urun(2,"A Mouse Optic",11.2,DateTime.Now),
new Urun(3,"Y Mouse Optic, Kablolu",9.49,DateTime.Now),
new Urun(5,"Z Mouse",3.45,new DateTime(2008,2,3)),
};
//
DataContractSerializer nesne örneğini oluşturulurken parametre
olarak Urun tipinden dizi verilmiştir
DataContractSerializer dcArraySerializer = new
DataContractSerializer(typeof(Urun[]));
FileStream stream3
= new FileStream("Urunler.xml", FileMode.Create,
FileAccess.Write);
dcArraySerializer.WriteObject(stream3, urunler);
stream3.Close();
#endregion
#region
DataContractSerializer ile Array Ters-Serileştirme
if
(File.Exists("Urunler.xml"))
{
DataContractSerializer dcDeSerializer = new
DataContractSerializer(typeof(Urun[]));
FileStream stream6 = new FileStream("Urunler.xml",
FileMode.Open, FileAccess.Read);
Urun[]
gelenUrunler = (Urun[])dcDeSerializer.ReadObject(stream6);
Console.WriteLine("Ürünler");
foreach (Urun urun in gelenUrunler)
{
Console.WriteLine("Id :{0} Ad:{1} Fiyat:{2} Stok Tarihi:{3}",
urun.Id.ToString(), urun.Ad, urun.ListeFiyati.ToString("C2"),
urun.StokTarihi.ToString());
}
stream6.Close();
}
#endregion
}
}
} |
Bu kod parçasında Urun tipine ait bir
nesne örneği ve bir dizinin serileştirilmesi ve serileşen dosya
içeriğinden tekrardan elde edilmesi(DeSerialization) işlenmektedir.
Serileştirme işleminde WriteObject metodu, ters serileştirme işleminde
ise ReadObject metodları kullanılmaktadır. Serileştirme ve
TersSerileştirme işlemlerinde tipe ait bilgilerin alınması için
DataContractSerializer nesne örneğinin üretimi sırasında devreye giren
yapıcı(Constructor) metoddan yararlanılır. Dikkat edileceği üzere,
Urun
ve Urun[] tip bilgileri yapıcı metoda parametre olarak verilmektedir.
ReadObject metodu, ilgili Stream üzerinden gelen veriyi okumakta ve ters
serileştirme işlemine tabi tutmaktadır. Elbette dönen veri türü object
olduğundan uygun tipe dönüştürülmesi gerekir. Örnekte dosya sistemi Stream olarak kullanılmaktadır. Uygulamanın çalışması sonrasında
serileştirme işlemi sonucu üretilen Urun.xml ve Urunler.xml
dosyalarının
içerikleri aşağıdaki gibi olacaktır.
Urun.xml içeriği;

Urunler.xml içeriği;

Çalışma zamanında oluşan ekran çıktısı
ise aşağıdaki gibidir. Dikkat edileceği üzere Stream içerisindeki
bilgilerden Urun ve Urun[] tipleri elde edilerek sonuçlar ekrana
yazdırılmaktadır.

Burada merak edilen noktalardan
biriside mesajlaşma sırasında kullanılan XSD şemasının içeriğinin ne
olduğudur. .Net Framework 3.0 ile birlikte gelen
XsdDataContractExporter tipinden yararlanılaraktan, söz konusu XSD şemasının
içeriği manuel olaraktan üretilebilir ve incelenebilir. Aşağıdaki kod
parçasında bu durum ele alınmaktadır.
// XSD
formatına dönüştürme yapacak tip tanımlanır
XsdDataContractExporter exporter = new
XsdDataContractExporter();
exporter.Options = new ExportOptions();
exporter.Export(typeof(Urun));
// Export işlemi
gerçekleştirilir
// Export işlemi sonucu oluşan XSD bilgisi herhangibir stream
üzerine aktarılabilir
FileStream stream = new FileStream("UrunSchema.xsd",
FileMode.Create, FileAccess.Write);
// Export edilen XmlSchemaSet içerisindeki tüm XmlSchema tipleri
dolaşılır
foreach (XmlSchema
set in
exporter.Schemas.Schemas())
{
// Çıktının kolay anlaşılır olması açısından o anki XmlSchema
örneğinin TargetNamespace özelliğinin değerinin DataContract
niteliğinde belirtilen Namespace bilgisine eşit olup olmadığına
bakılır
if
(set.TargetNamespace == "http://www.bsenyurt.com/Urun")
set.Write(stream);
// Eşit ise XmlSchema içeriği dosyaya yazılır
}
stream.Close(); |
Bu kodun çalışması sonucu aşağıdaki
ekran görüntüsünde yer alan XSD şemasının üretildiği görülür. (Burada
if ifadesi kaldırıldığı takdirde içerikte oluşacak farklılığın izlenmesi
ve analiz edilmesi önerilir.)

Görüldüğü gibi Urun isimli karmaşık tip
içerisindeki ilkel tiplerin(Primitive Type) tamamı, XSD karşılıklarına çevrilerek ele
alınmaktadır. İşte DataContractFormatter ile serileştirilen bir Urun
nesne örneği iki uygulama arasında hareket ederken bu şema bilgisinden
yararlanılmaktadır.
NetDataContractSerializer ile
Serileştirme
Bazı durumlarda hem istemci hemde
sunucu tarafında veri sözleşmelerinin aslına uygun şekilde kullanıldığı
durumlar söz konusu olabilir. Bu özellikle aynı tip .Net uygulamaların
olduğu senaryolarda söz konusudur. Bu sebepten WCF içerisinde buna
destek olacak şekilde NetDataContractSerializer tipi kullanılmaktadır.
Ne yazıkki WCF' in bu tipe doğrudan desteği yoktur. Bu sebepten ekstra
kod yazılması ve WCF tarafında serileştirilecek olan tipin çalışma
zamanı için özel bir nitelik(attribute) tasarlanması gerekmektedir. Yukarıda tasarlanan
örnek için NetDataContractSerializer aşağıdaki kod parçasında olduğu
gibi kullanılabilir.
using System;
using System.IO;
using
System.Runtime.Serialization;
namespace Formatters
{
class Program
{
static void Main(string[] args)
{
#region
NetDataContractSerializer ile Type Serileştirme
// Urun
tipine ait nesne örneği oluşturulur.
Urun lcd =
new Urun(9, "LCD 17inch", 125.45, DateTime.Now);
//
NetDataContractSerializer nesnesi örneklenir.
NetDataContractSerializer netSerializer = new
NetDataContractSerializer();
//
Serileştirmenin yapılacağı fiziki dosyayı işaret eden Stream
açılır.
FileStream
stream4 = new FileStream("NetUrun.xml", FileMode.Create,
FileAccess.Write);
//
WriteObject metodu ile lcd isimli Urun nesne örneği, stream4 ile
belirtilen Stream üzerine aktarılır.
netSerializer.WriteObject(stream4, lcd);
// Stream
kapatılır
stream4.Close();
#endregion
#region
NetDataContractSerializer ile Type Ters Serileştirme
//
NerUrun.xml dosyası var ise işlemleri yap.
if
(File.Exists("NetUrun.xml"))
{
// NetDataContractSerializer nesnesi örneklenir
NetDataContractSerializer netDeSerializer = new
NetDataContractSerializer();
// FileStream nesnesi örneklenir
FileStream stream7 = new FileStream("NetUrun.xml",
FileMode.Open, FileAccess.Read);
// ReadObject metodu parametre olarak ters serileştirilecek tipe
ait verileri taşıyan Stream örneğini alır.
// Metod geriye object tipini döndürdüğü için cast işlemi
yapılır.
Urun
gelenLcd = (Urun)netDeSerializer.ReadObject(stream7);
// Elde edilen nesneye ait bilgiler ekrana yazdırılır
Console.WriteLine("Id :{0} Ad:{1} Fiyat:{2} Stok Tarihi:{3}",
gelenLcd.Id.ToString(), gelenLcd.Ad,
gelenLcd.ListeFiyati.ToString("C2"),
gelenLcd.StokTarihi.ToString());
// Stream kapatılır
stream7.Close();
}
#endregion
#region
NetDataContractSerializer ile Array Serileştirme
Urun[] urunler ={
new Urun(2,"A Mouse Optic",11.2,DateTime.Now),
new Urun(3,"Y Mouse Optic, Kablolu",9.49,DateTime.Now),
new Urun(5,"Z Mouse",3.45,new DateTime(2008,2,3)),
};
NetDataContractSerializer netdcArraySerializer = new
NetDataContractSerializer();
FileStream
stream8 = new FileStream("NetUrunler.xml", FileMode.Create,
FileAccess.Write);
netdcArraySerializer.WriteObject(stream8, urunler);
stream8.Close();
#endregion
#region
NetDataContractSerializer ile Array Ters Serileştirme
if
(File.Exists("NetUrunler.xml"))
{
NetDataContractSerializer netdcDeSerializer = new
NetDataContractSerializer();
FileStream stream9 = new FileStream("NetUrunler.xml",
FileMode.Open, FileAccess.Read);
Urun[]
gelenUrunler = (Urun[])netdcDeSerializer.ReadObject(stream9);
Console.WriteLine("Net Ürünler");
foreach (Urun urun in gelenUrunler)
{
Console.WriteLine("Id :{0} Ad:{1} Fiyat:{2} Stok Tarihi:{3}",
urun.Id.ToString(), urun.Ad, urun.ListeFiyati.ToString("C2"),
urun.StokTarihi.ToString());
}
stream9.Close();
}
#endregion
}
}
} |
Örnekte Urun ve Urun[] dizilerinin
NetDataContractSerializer kullanılarak serileştirme işlemlerine tabi
tutulması ele alınmaktadır. Dikkat edileceği üzere
NetDataContractSerializer tipine ait nesneler örneklenirken
DataContractSerializer' da olduğu gibi tip bildirimi yapılmamaktadır.
Bunun sebebi, serileştirilen tipin zaten uygulama tarafındaki ilgili
assembly içerisinde var olmasıdır. Diğer taraftan yine WriteObject metodu ile serileştirme, ReadObject
metodu ilede ters serileştirme işlemleri gerçekleştirilmektedir.
Üretilen NetUrun.xml ve NetUrumler.xml dosyalarının içerikleri
ise aşağıda
görüldüğü gibidir.
NetUrun.xml içeriği;

NetUrunler.xml içeriği;

Dikkat edileceği üzere
DataContractSerializer ile üretilen XML çıktılarında farklı bir sonuç
oluşmaktadır. Herşeyden önce z:Assembly niteliği içerisinde Urun tipinin
yer aldığı Assembly bilgisi bulunmaktadır. Bunun önemli bir sonucu
vardır. Serileşen içeriğin ele alındığı taraflarda Urun tipinin yer
aldığı Assembly' ın var olması şarttır ki aslında bu durum SOA
mimarisinin uygulanış biçimlerinde tercih edilen yollardan birisi
değildir. İçerikte göze çarptan noktalardan biriside z:Id isimli
niteliklerdir(Attributes). Bu niteliklerin sadece referans tiplerine
uygulandığına dikkat edilmelidir(String,Array gibi). z:Id temel olarak
referans tiplerinin korunmasına yönelik olarak kullanılmaktadır. Bir
diğer dikkat çekici nitelik ise Urun[] dizisinin serileştirilmesi
sonrası ortaya çıkan z:Size bilgisidir. Buradada serileştirilen dizi
içerisindeki eleman sayısı yer almaktadır. Özet olarak
NetDataContractSerializer, WCF alt yapısında doğrudan
desteklenmemektedir. Her iki taraftada tipe ait Assembly bilgisinin
olmasını gerektirmektedir. Üstüne üstelik nesne örneğinin bilinen .Net
CLR tiplerine eşleştirilerek XML içeriğine alınmasını sağlamaktadır. Bu
sebeplerden dolayı ilgili serileştiricinin kullanılması pek yaygın
değildir.
DataContractJsonSerializer ile
Serileştirme
Bilindiği üzere .Net Framework 3.5 ile
birlikte WCF mimarisinde Ajax tabanlı istemciler için destek
gelmektedir. Bu desteğin temelinde ise JSON(JavaScript Object Notation)
formatlı veriler söz konusudur. Bu noktada WCF alt yapısı
DataContractJsonSerializer tipini ele almaktadır. Bu tipte serileştirme
desteği için WebScriptEnablingBehavior niteliğinin veya WebHttpBehavior
niteliğinde çözümle için JSON tipinin seçilmesi yeterlidir. Söz konusu
serileştirme tipi sayesinde Javascript, Asp.Net Ajax ve Silverlight
tabanlı web uygulmalarına destek verilebilmektedir. Aşağıdaki kod
parçasında yukarıdaki örneklerde kullanılan Urun ve Urun[] dizi
örneklerinin JSON serileştirilmesi ele alınmaktadır.
(DataContractJsonSerializer tipi System.Runtime.Serialization.Json
isim alanı(Namespace) altında yer almaktadır. Ancak bu isim alanına erişebilmek için
projeye System.ServiceModel.Web.dll assembly' ının referans edilmesi
gerekmektedir.)
using System;
using System.IO;
using System.Xml.Schema;
using
System.Runtime.Serialization;
using
System.Runtime.Serialization.Json;
namespace Formatters
{
class Program
{
static void Main(string[] args)
{
#region
DataContractJsonSerilazier ile Type Serileştirme
//
Serileştirilecek nesne örneği.
Urun mouse =
new Urun(1, "Mx Mouse Optic", 10, new DateTime(2004, 1, 1));
//
Serileştirici sınıf örneklenir.
// Parametre
olarak serileştirilecek veri sözleşmesi tipi verilir.
DataContractJsonSerializer dcJsonSerializer = new
DataContractJsonSerializer(typeof(Urun));
//
Serileştirme sonuçlarının yazılacağı örnek bir Stream
oluşturulur.
FileStream
stream10 = new FileStream("UrunJson.xml", FileMode.Create,
FileAccess.Write);
//
WriteObject metodu ile stream10 nesnesi ile tanımlanan Stream
üzerine mouse isimli Urun nesne örneği verisi aktarılır
dcJsonSerializer.WriteObject(stream10, mouse);
stream10.Close(); // Stream kapatılır.
#endregion
#region
DataContractJsonSerializer ile Type Ters-Serileştirme
//
UrunJson.xml dosyası var ise ters serileştirme işlemleri
yapılır.
if
(File.Exists("UrunJson.xml"))
{
// DataContractJsonSerializer nesnesi örneklenir.
DataContractJsonSerializer dcJsonDeSerializer = new
DataContractJsonSerializer(typeof(Urun));
// Serileştirilmiş veri içeriğinin bulunduğu dosyayı okumak için
bir Stream örneklenir.
FileStream stream11 = new FileStream("UrunJson.xml",
FileMode.Open, FileAccess.Read);
// ReadObject metodu ile parametre olarak verilen Stream
içerisindeki bilgi ters serileştirme işlemine tabi tutulur.
// Dönüş türü object olduğu için sonucun uygun türe cast
edilmesi gereklidir.
Urun urn
= (Urun)dcJsonDeSerializer.ReadObject(stream11);
// Elde edilen nesne örneği bilgisi ekrana yazdırılır.
Console.WriteLine("Id :{0} Ad:{1} Fiyat:{2} Stok Tarihi:{3}",
urn.Id.ToString(), urn.Ad, urn.ListeFiyati.ToString("C2"),
urn.StokTarihi.ToString());
// Stream kapatılır.
stream11.Close();
}
#endregion
#region
DataContractJsonSerializer ile Array Serileştirme
Urun[] urunler ={
new Urun(2,"A Mouse Optic",11.2,DateTime.Now),
new Urun(3,"Y Mouse Optic, Kablolu",9.49,DateTime.Now),
new Urun(5,"Z Mouse",3.45,new DateTime(2008,2,3)),
};
DataContractJsonSerializer dcJsonArraySerializer = new
DataContractJsonSerializer(typeof(Urun[]));
FileStream
stream12 = new FileStream("UrunlerJson.xml", FileMode.Create,
FileAccess.Write);
dcJsonArraySerializer.WriteObject(stream12, urunler);
stream12.Close();
#endregion
#region
DataContractJsonSerializer ile Array Ters-Serileştirme
if
(File.Exists("UrunlerJson.xml"))
{
DataContractJsonSerializer dcJsonDeSerializer = new
DataContractJsonSerializer(typeof(Urun[]));
FileStream stream13 = new FileStream("UrunlerJson.xml",
FileMode.Open, FileAccess.Read);
Urun[]
gelenUrunler = (Urun[])dcJsonDeSerializer.ReadObject(stream13);
Console.WriteLine("JSON Ürünler");
foreach (Urun urun in gelenUrunler)
{
Console.WriteLine("Id :{0} Ad:{1} Fiyat:{2} Stok Tarihi:{3}",
urun.Id.ToString(), urun.Ad, urun.ListeFiyati.ToString("C2"),
urun.StokTarihi.ToString());
}
stream13.Close();
}
#endregion
}
}
} |
DataContractJsonSerializer sınıfına ait
nesne örnekleri kullanılılırken yapıcı metoda, type bilgisi
verilmektedir. DataContractSerializer tipine benzer olaraktan WriteObject ve
ReadObject metodları ile stream üzerine serileştirme
yapmak ve stream üzerinden serileştirilen bilgileri okumak mümkündür. Kodun çalışması sonrası üretilen
çıktılar ise aşağıdaki gibidir.
UrunJson.xml içeriği;

UrunlerJson.xml içeriği;

JSON çıktısı, XML çıktısına göre daha
az yer tutmaktadır. Bununla birlikte çıktının okunurluğu çok daha
kolaydır. Dikkat edileceği üzere bilgiler key:value çiftleri şeklinde
yazılmıştır. Buda özellikle javascript tarafında veriye erişimde büyük
kolaylık sağlamaktadır.
XmlSerializer ile Serileştirme
.Net Framework 2.0 ile birlikte gelen
tiplerden birisi olan XmlSerializer ile, serileştirme adımlarını
özelleştirmek mümkündür. Diğer taraftan tüm Asp.Net Web Service modeli,
XmlSerializer üzerine kurulmuştur. Bu nedenle Asp.Net Web Service
uygulamalarının WCF tarafına aktarılmasında kolaylık sağlamaktadır.
Diğer taraftan bazı vakalarda tiplerin kaynak kodlarına erişilemediği
veya yeniden derleme işlemlerinin yapılamadığı durumlar bulunmaktadır.
Bu durumlarda XmlSerializer tipinden faydalanılabilir. Nitekim bu
vakalarda tiplere DataContract yada DataMember niteliklerinin
uygulanması söz konusu değildir.
XmlSerializer ile temel olarak üç
farklı modelde serileştirme işlemi gerçekleştirilebilir. İlk modele göre
varsayılan yapıcı(Default Constructor) kullanılır ve tipin public olan
özellik(Property) veya alanları(Field) serileşir. Diğer modelde
serileşen üyelerin çıktılarının özelleştirilmesi amacıyla XmlElement,
XmlAttribute gibi nitekiklerden yararlanılır. Son modelde ise
IXmlSerializable arayüzü(Interface) implemantasyonu gerçekleştirilerek
serileştirme işleminin özelleştirilmesi sağlanır.
 |
WCF tarafında tanımlı bir
servis sözleşmesinin(Service Contract) çıktılarda XmlSerializer modelini
kullanması için XmlSerializerFormat niteliğinden yararlanılması
gerekmektedir. |
Karar Vermek
Eğer serileştirme işlemi sırasında
serileşen tipe ait özel işlemler yapılması isteniyorsa XmlSerializer
tipinden yararlanılabilir. Bununla birlikte istemcilerin AJAX(AsynchronousJavascriptAndXml) veya
Silverlight tabanlı olmaları
halinde JSON formatında serileştirmeyi tercih etmekte yarar
bulunmaktadır. NetDataContractFormatter tipi ne yazıkki her iki
taraftada serileşecek tipe ait Assembly' ın olmasını ve özel kodlamalar
yapılmasını gerektirdiğinden(Nitekim çalışma zamanına bu
serileştiricinin kullanılacağının söylenmesi için özel attribute
yazılması gerekmektedir) çok fazla tercih edilmemektedir. Bunların
dışında kalan varsayılan durumlarda ise WCF zaten otomatik olarak
DataContractFormatter tipinden yararlanmaktadır. Bu sebepten
geliştiricinin sadece DataContract ve DataMember niteliklerini
kullanması yeterlidir.
Versiyonlama(Versioning)
Buraya kadarki kısımda WCF tarafında
ele alınabilecek olan serileştirici tiplerden kısaca bahsedilmiştir.
Serileştirme sürecinde önem arz eden konulardan biriside
versiyonlamadır. Nitekim eski istemcilerin yeni servis ile yada tam
tersine, eski servislerin yeni istemciler ile çalışması gerektiği
durumlar söz konusu olabilir. Servis ve istemci tarafının aynı
veri
sözleşmesine(Data Contract) sahip oldukları durumlarda versiyon
farklılıklarına karşı hazırlıklı olmaları gerekebilir. WCF tarafında
versiyonlama problemi aslında sessiz bir şekilde görmezden
gelinmektedir. Ancak yinede veri sözleşmelerine ait versiyon
farklılıkları olduğunda, vakanın nasıl ele alınması gerektiğinin
bilinmesinde yarar vardır. Temel olarak üç farklı versiyonlama senaryosu
bulunmaktadır.
Yeni Üyeler(New Members)

Bu senaryoda taraflardan birisi veri
sözleşmesinin yeni versiyonunu karşı tarafa göndermektedir. Yeni üyelere
sahip sözleşemeyi gönderen tarafın istemci veya servis olması
farketmemektedir. Bu vakada alıcı taraf, gelen yeni üyeleri görmezden
gelmektedir. Örneğin yukarıdaki şekilde taraflardan birisi Urun tipinin
yeni bir versiyonunu karşı tarafa göndermektedir. Yeni versiyonda Id,Ad
ve Fiyat alanlarına ek olarak Stok isimli yeni bir alan daha
bulunmaktadır. Karşı tarafta ise bu alan görmezden gelinir. Yinede yeni
versiyona ait veri içeriğinin tamamı karşı tarafa gönderilmekte ve ters
serileştirme(Deserializing) işlemi sırasında gelen yeni üye içerikleri
atlanılmaktadır(Ignore).
Kayıp Üyeler(Missing Members)

Özellikle eski veri sözleşmelerini
kullanan istemciler ile servis tarafında yeni veri sözleşmesinin yer
aldığı durumlarda söz konusudur. Burada servis tarafı istemciden gelen
eksik üyelerin yer aldığı veri sözleşmesini ters serileştirme işlemine
tabi tutarken varsayılan değerlerin atanmasını sağlamaktadır. Bir başka
deyişle yine sessiz bir şekilde versiyon farkı görmezden gelinir ve
eksik üyeler için ilk değerler atanır. Elbette OnDeserializing niteliği
ile imzalanmış bir metoddan yararlanılarak eksik üyeler için farklı
değerlerin verilmesi sağlanabilir. Tabi bu senaryoda ekstra bir durumda
vardır. Bilindiği üzere DataMember niteliğinin IsRequired özelliği
bulunmaktadır. Bu özelliğe göre ilgili üyeye ait değerin istemci
tarafından gelmesi şarttır. Bu durum ilerleyen kısımlardaki örneklerde
irdelenmektedir.
Round-Trip

Bu durumda istemci yeni üyelere sahip
veri sözleşmesi içeriğini servis tarafına göndermektedir. Ancak servis
tarafıda operasyon çağrısı sonucunda geriye aynı veri tipini
döndürmektedir. Servis tarafı, ters serileştirme işlemi sırasında gelen
fazla üyeyi kesip dışarıda bıraktığı için, operasyon sonucunda istemci
tarafına eksik veri içeriği gönderilecektir. Diğer tarafan senaryo
aşağıdaki şekildeki gibi olabilir.

Burada istemci tarafında veri
sözleşmesinin yeni üyeler içeren bir versiyonu bulunmaktadır. Urun
sınıfında yer alan Stok isimli özellik yeni üyedir. Ancak Service
1
tarafında Stok özelliğine sahip olmayan eski veri sözleşmesi
kullanılmaktadır. Bu durumda Service 1, doğal olaraktan Stok özelliğini
ve değerini görmezden gelecektir. Ne varki Service 1 kendisine gelen
Urun nesne verisini kırptıktan sonra, son haliyle Service
2 tarafına
göndermektedir. Oysaki Service 2 tarafında Urun tipine ait yeni veri
sözleşmesi yer almaktadır. Bu durumdada Stok özelliğine varsayılan ilk
değer atanır. Dolayısıyla istemcinin ilk etapta Stok değeri tamamen
ortadan kaybolmaktadır. Burada istemci ve Service 1 arasında
New Members
versiyonlama koşulları; Service 1 ile Service
2 arasında ise Missing Members versiyonlama koşulları oluşmaktadır.
Round-Trip
senaryosunda, IExtensibleDataObject arayüzünden yararlanılarak durumun
belirli ölçülerde kontrol altına alınmasıda sağlanabili ki ilerleyen
örneklerde bu durumda incelenmektedir.
Böylece geldik WCF mimarisinde
serileştirme, versiyonlama ve çözümleme ile ilgili ilk makalemizin sonuna.
Devam eden makalemizde versiyonlama koşullarını örnekler üzerinden
incelemeye çalışacağız. Özellikle Round-Trip hallerinde IExtensibleDataObject arayüzü kullanımını,
IsRequired özelliğinin
değerinin Missing Members vakasına olan etkisini, serileştirilen tipler
için vekil(Surrogate) tip kullanımını ve çözümleyiciler(Encoders) arasında karar
vermede dikkat edilmesi gereken hususları irdeliyor olacağız. Bir
sonraki makalemizde görüşünceye dek hepinize mutlu günler dilerim.
Örnek Uygulama için Tıklayın
Burak Selim ŞENYURT
selim@bsenyurt.com