DataContractSerializer zamiast czystego XML’a

Ostatnio kilka razy widziałem kod podobny do:

        public void SetConverterForLanguage(string language, string converterFilePath)
        {
            using (var reader = XmlReader.Create(converterFilePath, new XmlReaderSettings
            {
                IgnoreWhitespace = true
            }))
            {
                reader.Read();
                while (reader.Read())
                {
                    if (LanguageSettingsExist(language, reader))
                    {
                        UpdateConverter(reader);
                        return;
                    }
                }
            }
        }

        private static bool LanguageSettingsExist(string language, XmlReader reader)
        {
            return reader.NodeType == XmlNodeType.Element && reader.Name == "Converter"
                && reader["language"] == language;
        }

        // itd z podobnymi operacjami na XmlNode czy XmlAttribute

xml

Rzezanie w czystym xml’u nie jest raczej przyjemne. Używnia XmlNode zamiast stringów nie jest wielkim pocieszem. Nie tędy droga. Dużo prościej stworzyć sobie obiekty które następnie będziemy serializować (deserializować) do XMLa.

Podejście obiektowe

Tworzymy eleganckie w swej prostocie DTO:

    public class Converter
    {
        public string Language { get; set; }
        public string LettersToOmit { get; set; }
        public string Mapping { get; set; }
    }

Jedyne co jeszcze potrzebujemy to statyczna klasa oferująca generyczne funkcje. Na przykład taka jak poniższa (reużywalna):

    public static class XmlFileSerializer
    {
        public static void WriteToFile<T>(T data, string filePath)
        {
            var xmlSettings = new XmlWriterSettings { Indent = true };
            using (var writer = XmlWriter.Create(filePath, xmlSettings))
            {
                new DataContractSerializer(typeof(T)).WriteObject(writer, data);
            }
        }

        public static T ReadFromFile<T>(string filePath)
        {
            using (var reader = File.OpenRead(filePath))
            {
                return (T)new DataContractSerializer(typeof(T)).ReadObject(reader);
            }
        }
    }

Kod zajmuje mniej (pomijam klasę XmlFileSerializer), ale przede wszystkim unikamy niebezpiecznych konstrukcji typu:

    return reader.NodeType == XmlNodeType.Element && reader.Name == "Converter"
        && reader["language"] == language;

Zamiast tych kombinacji operujemy na danych korzystając z LINQ:

    var converters = XmlFileSerializer.ReadFromFile(converterFilePath);

    var converter = converters.FirstOrDefault(c => c.Language == language);

    if (converter == null)
        return;

    // itd już w lepszym obiektowym podejściu

Porównanie plików xml

Przed:

<Converters>
  <Converter language="pl" mapping="(s|z), (t|d), n, m, r, l, j, k, (f|w), (p|b)" lettersToOmit="[qeyuiohgaxcvQEYUIOHGAXCV]*" />
</Converters>

Po:

<ArrayOfConverter xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://schemas.datacontract.org/2004/07/Backend">
  <Converter>
    <Language>pl</Language>
    <LettersToOmit>[qeyuiohgaxcvQEYUIOHGAXCV]*</LettersToOmit>
    <Mapping>(s|z), (t|d), n, m, r, l, j, k, (f|w), (p|b)</Mapping>
  </Converter>
</ArrayOfConverter>

Jeśli chodzi o zastosowanie Extensible Markup Language, to drugie rozwiązanie jest bardziej poprawne ponieważ atrubuty mają opisysywać dane a nie je zawierać. Dane powinniśmy trzymać w kolejnych encjach.

Oczywiście w pierwszym podejściu programista mógł zamiast w atrybutach trzymać dane w osobnych elementach. Nie zrobił tego jednak, co pokazuje, że drugie podejście wymusza niejako lepsze rozwiązania.

XmlSerializer vs DataContractSerializer

Odradzam korzystanie z klasy XmlSerializer. Ona się chyba po prostu zestarzała, ma więcej wad i ograniczeń niż zalet, a jej następczynią jest właśnie DataContractSerializer.

Reklamy
Ten wpis został opublikowany w kategorii Programowanie i oznaczony tagami , , . Dodaj zakładkę do bezpośredniego odnośnika.

3 odpowiedzi na „DataContractSerializer zamiast czystego XML’a

  1. Pingback: dotnetomaniak.pl

  2. Brakuje mi jeszcze tylko porównania wydajności tych dwóch rozwiązań. O ile w zastosowaniach typu WebApi do wprowadzenia zamówienia do sklepu internetowego nie powinno to rodzić problemów o tyle przy replikacji danych po xml’u (np. z pół-offline’owej aplikacji mobilnej) może nastręczać pewnych kłopotów. Przy czym są to tylko moje przypuszczenia a nie wiedza tajemna ;]

Możliwość komentowania jest wyłączona.