XML w C#: Serializacja obiektów do XMLa
- Kategoria: Programowanie, Tagi: XML, C#, Person, .Net
- Napisane przez: Jerzy Piechowiak
- Liczba odsłon: 11677
XML jest jednym z najlepszych i najpopularniejszych formatów do wymiany danych. Jego implementacji, nie mogło zabraknąć również w C#. Warto wspomnieć, że XML pełni w platformie .NET bardzo ważną funkcję, ponieważ jest on często używany w celach konfiguracyjnych. Przykładem tego, jest na pewno dobrze wszystkim znany plik Web.config, używany do konfiguracji aplikacji ASP.NET.
Dzisiejszym wpisem, chciałbym rozpocząć mały cykl artykułów na temat wykorzystania XML w C#. Na pierwszy ogień, najprostszy sposób na tworzenie XMLi - serializacja.
Czym jest serializacja i do czego mi się może przydać
Ogólnikowo mówiąc, serializacja jest procesem, który pozwala na przedstawienie danych w inny, czasem spójniejszy, uporządkowany sposób. Serializując obiekty do XMLa, możemy je przedstawić w strukturze drzewiastej, charakterystycznej właśnie dla tego formatu.
A co do czego może się przydać serializacja? Prawdopodobnie jednym z najczęstszych jej zastosowań, jest eksport obiektów utworzonych w aplikacji. Jeśli wynik działania aplikacji zapiszemy w XMLu, to nic nie stoi na przeszkodzie, by przy ponownym uruchomieniu programu, wczytać ten plik i uzyskać dostęp do wcześniej utworzonych danych.
Eksportując dane do XMLa, będziemy mogli stworzyć również ładne raporty, które możemy zaprezentować użytkownikom. Oczywiście nie zrobimy tego tylko w oparciu o XML, ale XML może być źródłem dla transformacji XSLT (o transformacji postaram się napisać w następnej części), której wynikiem będzie piękny i zgrabny HTML:)
Jeśli do kogoś nie dociera jeszcze fajność serializacji, to mam nadzieję, że przekona go poniższy przykład, w którym zserializujemy trochę danych:)
Nieśmiertelna klasa Person
Aby rozpocząć serializację, musimy najpierw posiadać klasę, która przechowywać będzie jakieś dane. Kilkukrotnie już na tym blogu, wykorzystałem prostą stworzoną na potrzeby wpisów klasę Person (ostatnio w tekście Json.NET - opis biblioteki. Wykorzystam ja również i dziś, z tym że wprowadzę do niej małe modyfikacje.
public class Person
{
public int PersonId { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public int Age { get; set; }
public Person()
{
}
public Person(int nPersonId, string sFirstName, string sLastName, int nAge)
{
PersonId = nPersonId;
FirstName = sFirstName;
LastName = sLastName;
Age = nAge;
}
}
To na co warto zwrócić uwagę na powyższym listingu, to kilka cech, którymi musi się charakteryzować klasa podlegająca serializacji:
- Elementy, które mają zostać zseralizowane, muszą posiadać publiczne właściwości
- Klasa ulegająca serializacji, musi posiadać obowiązkowo pusty, bezparametrowy konstruktor
- Procesem serializacji można sterować przy pomocy atrybutów serializacji
Właściwości użyte w kodzie, będą również nazwami odpowiednich węzłów w wyjściowym pliku XML. Nazwy węzłów mogą zostać jednak zmienione za pomocą atrybutów serializacji. Ponieważ jest to szersze zagadnienie, napiszę o tym więcej w kolejnym akapicie.
Atrybuty serializacji
Jak wspomniałem w akapicie wyżej, procesem serializacji możemy sterować za pomocą atrybutów serializacji. Każdy z takich atrybutów, należy zapisać w kwadratowych nawiasach i umieścić bezpośrednio przed właściwością, której ma dotyczyć. Oczywiście robimy to w kodzie klasy danych.
Poniżej lista atrybutów, których możemy użyć
- [XmlElement("NazwaWezlaWXml")] - atrybut ten, pozwala na określenie nowej nazwy węzła dla wybranej właściwości
- [XmlAttribute("NazwaAtrybutuXml")] - czyni określoną właściwość atrybutem węzła nadrzędnego
- [XmlIgnore] - pomija wybraną właściwość w wynikowym pliku XML
- [XmlText] - tworzy węzeł tekstowy wewnątrz węzła nadrzędnego
Do czego można wykorzystać takie atrybuty? Jeden z przykładów to np. lokalizacja elementów klasy na określony język w wyjściowym pliku XML. Poniższy przykład pokazuje wykorzystanie atrybutów XmlElement oraz XmlAttribute. Oprócz lokalizacji węzłów, stworzymy z właściwości PersonId nowy atrybut elementu Person:
[XmlAttribute("Id")]
public int PersonId { get; set; }
[XmlElement("Imie")]
public string FirstName { get; set; }
[XmlElement("Nazwisko")]
public string LastName { get; set; }
[XmlElement("Wiek")]
public int Age { get; set; }
Serializacja
Po przygotowaniu danych oraz omówieniu możliwości ich modyfikacji w wyjściowym pliku XML, czas na przygotowanie kodu C#, który doprowadzi do samej serializacji.
Na początek usingi
:
using System; using System.Collections.Generic; using System.Xml.Serialization; using System.IO;
A teraz już właściwy kod:
List<Person> oPersonsList = new List<Person>();
int nCounter = 0;
oPersonsList.Add(new Person(++nCounter, "Jan", "Kowalski", 23));
oPersonsList.Add(new Person(++nCounter, "Agnieszka", "Nowak", 22));
XmlRootAttribute oRootAttr = new XmlRootAttribute();
oRootAttr.ElementName = "Persons";
oRootAttr.IsNullable = true;
XmlSerializer oSerializer = new XmlSerializer(typeof(List<Person>), oRootAttr);
StreamWriter oStreamWriter = null;
try
{
oStreamWriter = new StreamWriter("person.xml");
oSerializer.Serialize(oStreamWriter, oPersonsList);
}
catch (Exception oException)
{
Console.WriteLine("Aplikacja wygenerowała następujący wyjątek: " + oException.Message);
}
finally
{
if (null != oStreamWriter)
{
oStreamWriter.Dispose();
}
}
Jak widać, kod nie jest przesadnie skomplikowany:). W liniach 1-4 przygotowujemy przykładowe dane. Klasa obsługująca serializację jest niezwykle uniwersalna więc nic nie stoi na przeszkodzie by do pliku XML zapisać kolekcję określonych obiektów (czytaj więcej na temat kolekcji w C#). W liniach 5-7, ustawiamy właściwości tzw. korzenia dokumentu XML. Ponieważ przechowywać będziemy w nim obiekty klasy Person, logicznie będzie go nazwać Persons. Ustawiamy także właściwość IsNullable na true.
W linii 8, tworzymy obiekt klasy XmlSerializer. Posiada ona kilka konstruktorów. Ja przekażę tutaj typ danych wejściowych (wykorzystanie typeof) oraz informację o korzeniu dokumentu.
W liniach 10-25 znajduje się blok try-catch-finally dopełniający dzieła. Najpierw tworzymy obiekt klasy StreamWriter, który ma zapisać nasz wyjściowy XML. Następnie w linii 13 (cóż za pechowy zbieg okoliczności:)) dokonujemy serializacji. Do metody Serialaize przekazujemy obiekt strumienia oraz listę obiektów Person. Przekazane dane muszą być zgodne z tym co określiliśmy w linii 8.
Ponieważ złe rzeczy dzieją się czasem w kodzie, dlatego w linii 17 poinformujemy o potencjalnym błędzie.
Zwieńczeniem dzieła jest klauzula finally, w której znajduje się kod, który niezależnie od sytuacji zamknie otwarty nasz strumień:)
Podsumowanie
Tak jak wspomniałem we wstępie, był to pierwszy wpis z cyklu XML w C#. W kolejnym odcinku zamierzam opisać transformację XSLT, a w trzecim, który prawdopodobnie będzie ostatni, zajmę się sposobem odczytu danych w XMLu (DOM oraz XPath)
Data ostatniej modyfikacji: 05.06.2011, 17:18.
Komentarze