Artykuł

lip 18 2010
0

XML w C#: Serializacja obiektów do XMLa

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.

Podoba Ci się ten wpis? Powiedz o tym innym!

Send to Kindle

Komentarze

blog comments powered by Disqus