poniedziałek, 21 stycznia 2013

C#: serializacja i deserializacja obiektów do/z XElement

Istnieje co najmniej tyle pomysłów na przechowywanie ustawień aplikacji, co programistów. W projektach w języku Java bardzo chętnie korzystałem z plików ini. Łatwość parsowania plików XML w języku C# spowodowała zmianę nastawienia. Istnieje też masa pomysłów na organizację samego pliku. Są zwolennicy zapisywania konkretnych prostych wartości, są zwolennicy szeregowania obiektów. Poniżej proste przykłady kodu jak serializować i deserializować obiekty korzystając z klasy XEelement i LinQ przy pracy na XML.


Kod pochodzi z klasy ConfigManager w pewnym projekcie. Klasa ta odpowiada za ładowanie i zapisywanie dowolnych ustawień i wydaje mi się całkiem nieźle zaprojektowana. Szczególnie, że poza szeregowaniem i deszeregowaniem obiektów dowolnego typu, posiada również kontrolę nad plikiem ustawień, co pozwala na przeładowanie ustawień podczas pracy aplikacji, czy zapisywanie ustawień z aplikacji gdy plik z nimi zostanie usunięty podczas jej pracy. Proste i zmyślne mechanizmy.

Wracając jednak do tematu tego posta. Podstawowe dwie metody klasy ConfigManager to Load i Save. Obie są metodami generycznymi dzięki czemu możliwe jest zapisanie obiektu praktycznie dowolnej klasy. Poniżej najistotniejsze fragmenty kodu metody Save:

public bool Save<T>(string key, T value)
{
    XElement xRecord = null;
    XmlSerializer serializer = new XmlSerializer(typeof(T));
    StringWriter writer = new StringWriter();
    serializer.Serialize(writer, value);
    (...)
    xRecord.Add(XElement.Parse(writer.ToString());
    (...)
    return true;
}

Czego oczy nie widzą tego sercu nie żal. W zamieszczonej części kodu brakuje wyszukiwania właściwego elementu w dokumencie, lub tworzenia nowego. Brakuje też dodawania elementu do dokumentu i zapisywania samego dokumentu. Brakujący kod jest jednak łatwy do uzupełnienia i zależny od struktury dokumentu.

Wywołanie metody Save jest barzo proste i nie wymaga podawania typu, dzięki czemu jej zapis nie jest zbyt kobylasty.
ConfigManager.Save("LastClient", pClient);
Poniżej zamieszczam kod metody Load. Ponownie pozwolę sobie pominąć część kodu odpowiedzialną za wyszukiwanie elementu w dokumencie.

public T Load<T>(string key)
{
    XElement xRecord = null;
    (...)
    XmlSerializer serializer = new XmlSerializer(typeof(T));
    XElement xValue = xRecord.Elements().FirstOrDefault();
    if(xValue != null)
        return (T)serializer.Deserialize(new StringReader(xValue.ToString()));
}

Wywołanie metody Load wygląda tak:
Client last = ConfigManager.Load<client>("LastClient");
Jak widać nie jest konieczne rzutowanie typów. Główne o czym należy pamiętać, to rzutowanie dla obiektu zwracanego z metody Deserialize() klasy XmlSerializer. Istotne jest też, że nie może ona przyjąć obiektu klasy XElement, a jedynie obiekt implementujący interface IReader. W przypadku metody Serialize konieczny jest obiekt klasy implementujący interface IWriter. Jak widać serializacja i deserializacja  do XML są stosunkowo proste. Dzięki temu łatwo można przechowywać całe obiekty i się nie martwić. W skrajnym przypadku można przechować obiekt klasy Config.

Czytaj też:

Brak komentarzy:

Prześlij komentarz