C# + LINQ ile XML/RSS dosyasından bilgi okuma

Bu yazımızda C# ile XML dosyalarını okuma konusunu işleyeceğiz. Hem dosyadan hem de URL den okumayı göreceğiz.

XML okuma işlemlerini farklı yollardan yapabiliriz ama bu yazıda;

using System.Xml;
using System.Xml.Linq;
namespacelerini kullanacağız. Amacımız LINQ ile XML üzerinde sorgulamalar yapabilmek.
 
Önce üzerinde çalışacağımız bir XML dosyası ayarlayalım.
Bunun için http://www.w3schools.com/xml/cd_catalog.xml adresinde bulunan xml dosyasını /App_Data klasörünün altına xmldosya.xml adıyla kaydettim.
 
XML içeriği:
<?xml version="1.0" encoding="ISO-8859-1"?>
<!-- Edited by XMLSpy® -->
<CATALOG>
	<CD>
		<TITLE>Empire Burlesque</TITLE>
		<ARTIST>Bob Dylan</ARTIST>
		<COUNTRY>USA</COUNTRY>
		<COMPANY>Columbia</COMPANY>
		<PRICE>10.90</PRICE>
		<YEAR>1985</YEAR>
	</CD>
	<CD>
		<TITLE>Hide your heart</TITLE>
		<ARTIST>Bonnie Tyler</ARTIST>
		<COUNTRY>UK</COUNTRY>
		<COMPANY>CBS Records</COMPANY>
		<PRICE>9.90</PRICE>
		<YEAR>1988</YEAR>
	</CD>
	<CD>
		<TITLE>Greatest Hits</TITLE>
		<ARTIST>Dolly Parton</ARTIST>
		<COUNTRY>USA</COUNTRY>
		<COMPANY>RCA</COMPANY>
		<PRICE>9.90</PRICE>
		<YEAR>1982</YEAR>
	</CD>
	<CD>
		<TITLE>Still got the blues</TITLE>
		<ARTIST>Gary Moore</ARTIST>
		<COUNTRY>UK</COUNTRY>
		<COMPANY>Virgin records</COMPANY>
		<PRICE>10.20</PRICE>
		<YEAR>1990</YEAR>
	</CD>
	<CD>
		<TITLE>Eros</TITLE>
		<ARTIST>Eros Ramazzotti</ARTIST>
		<COUNTRY>EU</COUNTRY>
		<COMPANY>BMG</COMPANY>
		<PRICE>9.90</PRICE>
		<YEAR>1997</YEAR>
	</CD>
	<CD>
		<TITLE>One night only</TITLE>
		<ARTIST>Bee Gees</ARTIST>
		<COUNTRY>UK</COUNTRY>
		<COMPANY>Polydor</COMPANY>
		<PRICE>10.90</PRICE>
		<YEAR>1998</YEAR>
	</CD>
	<CD>
		<TITLE>Sylvias Mother</TITLE>
		<ARTIST>Dr.Hook</ARTIST>
		<COUNTRY>UK</COUNTRY>
		<COMPANY>CBS</COMPANY>
		<PRICE>8.10</PRICE>
		<YEAR>1973</YEAR>
	</CD>
	<CD>
		<TITLE>Maggie May</TITLE>
		<ARTIST>Rod Stewart</ARTIST>
		<COUNTRY>UK</COUNTRY>
		<COMPANY>Pickwick</COMPANY>
		<PRICE>8.50</PRICE>
		<YEAR>1990</YEAR>
	</CD>
	<CD>
		<TITLE>Romanza</TITLE>
		<ARTIST>Andrea Bocelli</ARTIST>
		<COUNTRY>EU</COUNTRY>
		<COMPANY>Polydor</COMPANY>
		<PRICE>10.80</PRICE>
		<YEAR>1996</YEAR>
	</CD>
	<CD>
		<TITLE>When a man loves a woman</TITLE>
		<ARTIST>Percy Sledge</ARTIST>
		<COUNTRY>USA</COUNTRY>
		<COMPANY>Atlantic</COMPANY>
		<PRICE>8.70</PRICE>
		<YEAR>1987</YEAR>
	</CD>
	<CD>
		<TITLE>Black angel</TITLE>
		<ARTIST>Savage Rose</ARTIST>
		<COUNTRY>EU</COUNTRY>
		<COMPANY>Mega</COMPANY>
		<PRICE>10.90</PRICE>
		<YEAR>1995</YEAR>
	</CD>
	<CD>
		<TITLE>1999 Grammy Nominees</TITLE>
		<ARTIST>Many</ARTIST>
		<COUNTRY>USA</COUNTRY>
		<COMPANY>Grammy</COMPANY>
		<PRICE>10.20</PRICE>
		<YEAR>1999</YEAR>
	</CD>
	<CD>
		<TITLE>For the good times</TITLE>
		<ARTIST>Kenny Rogers</ARTIST>
		<COUNTRY>UK</COUNTRY>
		<COMPANY>Mucik Master</COMPANY>
		<PRICE>8.70</PRICE>
		<YEAR>1995</YEAR>
	</CD>
	<CD>
		<TITLE>Big Willie style</TITLE>
		<ARTIST>Will Smith</ARTIST>
		<COUNTRY>USA</COUNTRY>
		<COMPANY>Columbia</COMPANY>
		<PRICE>9.90</PRICE>
		<YEAR>1997</YEAR>
	</CD>
	<CD>
		<TITLE>Tupelo Honey</TITLE>
		<ARTIST>Van Morrison</ARTIST>
		<COUNTRY>UK</COUNTRY>
		<COMPANY>Polydor</COMPANY>
		<PRICE>8.20</PRICE>
		<YEAR>1971</YEAR>
	</CD>
	<CD>
		<TITLE>Soulsville</TITLE>
		<ARTIST>Jorn Hoel</ARTIST>
		<COUNTRY>Norway</COUNTRY>
		<COMPANY>WEA</COMPANY>
		<PRICE>7.90</PRICE>
		<YEAR>1996</YEAR>
	</CD>
	<CD>
		<TITLE>The very best of</TITLE>
		<ARTIST>Cat Stevens</ARTIST>
		<COUNTRY>UK</COUNTRY>
		<COMPANY>Island</COMPANY>
		<PRICE>8.90</PRICE>
		<YEAR>1990</YEAR>
	</CD>
	<CD>
		<TITLE>Stop</TITLE>
		<ARTIST>Sam Brown</ARTIST>
		<COUNTRY>UK</COUNTRY>
		<COMPANY>A and M</COMPANY>
		<PRICE>8.90</PRICE>
		<YEAR>1988</YEAR>
	</CD>
	<CD>
		<TITLE>Bridge of Spies</TITLE>
		<ARTIST>T'Pau</ARTIST>
		<COUNTRY>UK</COUNTRY>
		<COMPANY>Siren</COMPANY>
		<PRICE>7.90</PRICE>
		<YEAR>1987</YEAR>
	</CD>
	<CD>
		<TITLE>Private Dancer</TITLE>
		<ARTIST>Tina Turner</ARTIST>
		<COUNTRY>UK</COUNTRY>
		<COMPANY>Capitol</COMPANY>
		<PRICE>8.90</PRICE>
		<YEAR>1983</YEAR>
	</CD>
	<CD>
		<TITLE>Midt om natten</TITLE>
		<ARTIST>Kim Larsen</ARTIST>
		<COUNTRY>EU</COUNTRY>
		<COMPANY>Medley</COMPANY>
		<PRICE>7.80</PRICE>
		<YEAR>1983</YEAR>
	</CD>
	<CD>
		<TITLE>Pavarotti Gala Concert</TITLE>
		<ARTIST>Luciano Pavarotti</ARTIST>
		<COUNTRY>UK</COUNTRY>
		<COMPANY>DECCA</COMPANY>
		<PRICE>9.90</PRICE>
		<YEAR>1991</YEAR>
	</CD>
	<CD>
		<TITLE>The dock of the bay</TITLE>
		<ARTIST>Otis Redding</ARTIST>
		<COUNTRY>USA</COUNTRY>
		<COMPANY>Atlantic</COMPANY>
		<PRICE>7.90</PRICE>
		<YEAR>1987</YEAR>
	</CD>
	<CD>
		<TITLE>Picture book</TITLE>
		<ARTIST>Simply Red</ARTIST>
		<COUNTRY>EU</COUNTRY>
		<COMPANY>Elektra</COMPANY>
		<PRICE>7.20</PRICE>
		<YEAR>1985</YEAR>
	</CD>
	<CD>
		<TITLE>Red</TITLE>
		<ARTIST>The Communards</ARTIST>
		<COUNTRY>UK</COUNTRY>
		<COMPANY>London</COMPANY>
		<PRICE>7.80</PRICE>
		<YEAR>1987</YEAR>
	</CD>
	<CD>
		<TITLE>Unchain my heart</TITLE>
		<ARTIST>Joe Cocker</ARTIST>
		<COUNTRY>USA</COUNTRY>
		<COMPANY>EMI</COMPANY>
		<PRICE>8.20</PRICE>
		<YEAR>1987</YEAR>
	</CD>
</CATALOG>

 

XML'i sabit bir dosyadan okuma:

Örnek olarak YEAR bilgisi 1990'dan büyük olan kayıtları filtreleyip GridView'da gösterelim.
 
Dikkat: Dosya yolunu gösterirken C:\klasor1\klasor2\klasor3\App_Data\xmldosya.xml gibi mutlak bir adres vermemiz gerekiyor. Çalışacağımız bilgisayarlarda / sunucularda klasörler farklılık gösterebileceği için Server.MapPath() ile göreceli olarak verdiğimiz dosya yolunu çalıştığımız bilgisayara uygun olarak mutlak adrese çevirmeliyiz.
 
 
string dosya = Server.MapPath("~/App_Data/xmldosya.xml");
XmlTextReader reader = new XmlTextReader(dosya);
XElement xel = XElement.Load(reader);

gv.DataSource = from x in xel.Elements("CD")
                            where Convert.ToInt32(x.Element("YEAR").Value) > 1990
                            select new
                            {
                                Title = x.Element("TITLE").Value,
                                Year = x.Element("YEAR").Value
                            };
gv.DataBind();
Sonuç:
 
Title Year
Eros 1997
One night only 1998
Romanza 1996
Black angel 1995
1999 Grammy Nominees 1999
For the good times 1995
Big Willie style 1997
Soulsville 1996
Pavarotti Gala Concert 1991
 
Aynı işlemi ASP.NET'te değil de Windows Formu'nda yapmak isteseydik değiştireceğimiz tek satır dosya yolunu tanımlama satırı olacaktı.
string dosya = Application.StartupPath + @"\Data\xmldosya.xml";
XmlTextReader reader = new XmlTextReader(dosya);
XElement el = XElement.Load(reader);

gerisi aynı.. :)

Application.StartupPath(), çalıştırdığımız programın yolunu döndürür.

 

URL'den XML dosyasını okuma:

Şimdi de sabit bir dosyadan değil de direkt olarak URL'den xml dosyasını okuyalım.

Örnek olarak, XML dosyasını kaydettiğimiz adresi kullanalım: http://www.w3schools.com/xml/cd_catalog.xml

string url = "http://www.w3schools.com/xml/cd_catalog.xml";
XElement xel = XElement.Load(url);
Bu kadar basit.
Hem Windows Formunda hem de ASP.NET sayfamızdan bu kodu rahatlıkla çalıştırabiliriz.
Tahmin edeceğiniz üzere sorgulama kodları yukarıdaki örnekten farklı olmayacak.
 
Şimdi de biraz ileri seviye konulara değinmek istiyorum.
XML tagları eğer bizim xml dosyasındaki gibiyse sorun yaşamadan her türlü işlemi gerçekleştirebiliriz.
Ancak elinizdeki XML dosyasında aşağıdaki gibi bir yapı da bulunabilirdi:
 
<?xml version="1.0" encoding="utf-8"?>
<rss xmlns:media="http://search.yahoo.com/mrss/" xmlns:dc="http://purl.org/dc/elements/1.1/" version="2.0">
  <channel>
    <title>Title bilgisi</title>
    <link>http://www.daltinkurt.com</link>
    <description>daltinkurt.com</description>
    <item>
      <title>Başlık 1</title>
      <link>http://www.daltinkurt.com/abc.aspx</link>
      <description>Açıklama, açıklama, ...</description>
      <category>GÜNCEL</category>
      <pubDate>Mon, 05 Nov 2012 16:56:53 GMT</pubDate>
      <guid>1234566</guid>
      <dc:creator>daltinkurt</dc:creator>
      <dc:date>2012-11-05T16:56:53Z</dc:date>
    </item>
    <item>
      <title>Başlık 2</title>
      <link>http://www.daltinkurt.com/xyz.aspx</link>
      <description>Açıklama, açıklama, ...</description>
      <category>GÜNCEL</category>
      <pubDate>Mon, 05 Nov 2012 16:51:48 GMT</pubDate>
      <guid>1234567</guid>
      <dc:creator>daltinkurt</dc:creator>
      <dc:date>2012-11-05T16:51:48Z</dc:date>
    </item>
...
  </channel>
</rss>
Yukarıdaki örnek xml dosyası, bir haber ajansının müşterilerine sunduğu haber servisinden alıntıdır. Tabii ki bilgileri kendime göre değiştirdim.
Bu dosyadaki bilgileri, yukarıdaki kodları kullanarak listelemeye çalışalım:
 
string dosya = Server.MapPath("~/App_Data/xmldosya.xml");
XmlTextReader reader = new XmlTextReader(dosya);
XElement xel = XElement.Load(reader);

gv.DataSource = from x in xel.Element("channel").Elements("item")
                select new
                {
                    Baslik = x.Element("title").Value,
                    Tarih = x.Element("dc:date").Value
                };
gv.DataBind();

 

Sonuç:

 

'/xml' Uygulamasında Sunucu Hatası.


':' karakteri (onaltılı değeri 0x3A), bir adın içinde bulunamaz.

Açıklama: Geçerli web isteği yürütülürken işlenmemiş özel durum oluştu. Lütfen hata ve kod içinde kaynaklandığı yer hakkında daha fazla bilgi almak için yığın izlemesini gözden geçirin.

Özel Durum Ayrıntıları: System.Xml.XmlException: ':' karakteri (onaltılı değeri 0x3A), bir adın içinde bulunamaz.

 

Yukarıdaki hatayı aldık. dc:date tagını kodlarımız kabul etmedi. Enteresan bir hata bence, değil mi? :)

Çözümü de son derece kolay. 

XML dosyamızın en üstünde

<rss xmlns:media="http://search.yahoo.com/mrss/" xmlns:dc="http://purl.org/dc/elements/1.1/" version="2.0">

şeklinde bir tanımlama satırı var. dc:date tagının başındaki dc önekini bu satırda hangi xml namespace ine ait olduğunu tanımlıyor.

O zaman, kodlarımızda ufak bir değişikliğe gidelim:

string dosya = Server.MapPath("~/App_Data/xmldosya.xml");
XmlTextReader reader = new XmlTextReader(dosya);
XElement xel = XElement.Load(reader);

XNamespace dc = "http://purl.org/dc/elements/1.1/";

gv.DataSource = from x in xel.Element("channel").Elements("item")
                select new
                {
                    Baslik = x.Element("title").Value,
                    Tarih = x.Element(dc + "date").Value
                };
gv.DataBind();

XNamespace ile dc öntanımının hangi namespace e denk geldiğini tanımlıyoruz ve artık dc:xxx barındıran tagları direkt değil de dc + "xxx" şeklinde kullanıyoruz. Hepsi bu.

Aynı dosya üzerinde bir de sorgulama yapalım:

string dosya = Server.MapPath("~/App_Data/xmldosya.xml");
XmlTextReader reader = new XmlTextReader(dosya);
XElement xel = XElement.Load(reader);

XNamespace dc = "http://purl.org/dc/elements/1.1/";

gv.DataSource = from x in xel.Element("channel").Elements("item")
                where x.Element(dc + "creator").Value.Equals("daltinkurt")
                select new
                {
                    Baslik = x.Element("title").Value,
                    Tarih = x.Element(dc + "date").Value
                };
gv.DataBind();

Mantığı anladınız sanırım.

Herkese bol C#'lı LINQ'li XML'li günler diliyorum. :)