Сериализация на данни



страница2/2
Дата10.04.2018
Размер405.33 Kb.
#66633
1   2

XML сериализация


До момента разгледахме класическата сериализация и десериализация на обекти. Нека сега се запознаем с още една възможност за съхраняване и възстановяване състоянието на обекти, която .NET Framwork предоставя на програмиста – XML сериализацията.

Какво е XML сериализация?


XML сериализация представлява записването на публичните полета на обект в XML формат с цел съхранение или пренасяне. Тя е част от вгра­де­на­та поддръжка на XML в .NET Framework. Обратният процес на XML сериализацията е XML десериализацията.

XML сериализацията създава някои ограничения, които трябва да имаме предвид. При нея се сериализират само публичните полета и не се запазва целостта на типа. XML сериализацията не може да се справи с циклично свързани графи от обекти. Могат да се сериализират всякакви обекти, но класът трябва да има конструктор без параметри.

Всъщност XML сериализацията не е сериализация в истинския смисъл на това понятие, защото не съхранява и възстановява пълното състояние на обектите, а само части от него.

XML сериализация – пример


В следващия пример ще илюстрираме как един клас може да сериализира данните си чрез XML сериализация:

public class Student

{

public string mName;



public int mAge;
public void SerializeToXml(Stream aStream)

{

XmlSerializer xmlSerializer =



new XmlSerializer(typeof(Student));

xmlSerializer.Serialize(aStream, this);

}
public static Student DeserializeFromXml(Stream aStream)

{

XmlSerializer xmlSerializer =



new XmlSerializer(typeof(Student));

Student st = (Student) xmlSerializer.Deserialize(aStream);

return st;

}

}


Как работи примерът?


Класът Student има две публични полета – mName и mAge. Те тряб­ва да са публични, за да могат да се запазят при XML сериализацията.

Реализирали сме метод SerializeToXml(…), който сериализира данните на класа в XML формат в подадения му като параметър поток. За целта създа­ваме обект от класа XmlSerializer и извикваме метода му Serialize(…), който сериализира инстанцията на класа в потока.

Методът DeserializeFromXml(…) служи за десериализиране на данните от подадения му като параметър поток. За целта създаваме обект от класа XmlSerializer и извикваме метода му Deserialize(…), който десе­ри­а­ли­зи­ра данните от потока и връща десериализирания обект.

Проста XML сериализация – пример


Ще представим още един по-подробен пример, илюстриращ въз­мож­нос­ти­те на .NET Framework за сериализация на обекти в XML формат чрез класа XmlSerializer:

using System;

using System.IO;

using System.Xml.Serialization;
public class Student

{

private string mName;



private int mAge;
public string Name

{

get { return mName; }



set { mName = value; }

}
public int Age

{

get { return mAge; }



set { mAge = value; }

}
public override string ToString()

{

string result =



String.Format("(Name: {0}, Age: {1})", Name, Age);

return result;

}

}
class XmlSerializationDemo



{

static void Main()

{

Student student = new Student();



student.Name = "Дядо Мраз";

student.Age = 99;

Console.WriteLine("Original = {0}", student);

// Serialize student object to "student.xml" file

XmlSerializer xmlSerializer =

new XmlSerializer(typeof(Student));

FileStream outputStream = File.OpenWrite("student.xml");

using (outputStream)

{

xmlSerializer.Serialize(outputStream, student);



}

Console.WriteLine("Student serialized.");


// Deserialize student object from "student.xml" file

FileStream inputStream = File.OpenRead("student.xml");

using (inputStream)

{

Student deserializedStudent =



(Student) xmlSerializer.Deserialize(inputStream);

Console.WriteLine("Student deserialized.");

Console.WriteLine("Deserialized = {0}",

deserializedStudent);

}

}

}


Как работи примерът?


В примера сме дефинирали класа Student, който има две публични свой­ства, които ще бъдат сериализирани. В класа е предефини­ран мето­дът ToString(), който връща символен низ, описващ съдържа­ние­то на обект от този тип. Този метод ще използваме за визуализация на Student обекти.

Във функцията Main() на класа XmlSerializationDemo създа­ва­ме обект от класа Student, инициализираме го и отпечатваме съ­дър­жа­ни­ето му в конзолата. След това създаваме обект от класа XmlSerializer и използваме метода му Serialize(…), за да сериализираме инстанцията на кла­са Student във файла student.xml. Накрая, използвайки метода Deserialize(…) на класа XmlSerializer, извършваме десериализацията от XML документ към инстанция на Student и отпечатваме съдържанието на тази инстанция в конзолата. Ето какъв е резултатът след изпълнението на при­мера:



Както виждаме, информацията е възстановена коректно. Оригиналният обект и обектът, получен след десериализацията, са еднакви. Ето как из­глеж­да и съдържанието на файла student.xml, в който е записан сериа­ли­зи­ра­ният обект:



Виждаме, че в XML файла са записани всички публични членове на сериа­ли­зи­рания Student обект.


Контролиране на изходния XML


Ако е нужно, можем да контролираме изходния XML, генериран от класа XmlSerializer. Това става чрез атрибути, които прилагаме към класа или към неговите полета. Ето кратък пример:

using System.Xml.Serialization;

public class OptionalOrder

{

[XmlElement(ElementName = "Tax_Rate")]



public decimal TaxRate;
[XmlAttribute]

public string FirstOrder;


[XmlIgnoreAttribute]

public bool FirstOrderSpecified;


[XmlArrayAttribute("Items")]

[XmlArrayItem("MemberName")]

public OrderedItem[] OrderedItems;
[XmlElement]

public Employee[] Employees;

}


В примера сме дефинирали класа OptionalOrder. Към полетата му сме прило­жили атрибути, чрез които указваме как да се запишат в XML – чрез XML елементи, чрез XML атрибути и др.

Чрез атрибутът XmlElement указваме, че полето, към което е приложен, трябва да се сериализира като XML елемент. Чрез него можем да контро­лираме характеристиките на XML елемента, като най-често го използва­ме за указване на името на елемента.

Атрибутът XmlAttribute указва, че полето, към което е приложен, трябва да се сериализира като XML атрибут. По подразбиране XmlSerializer сериализира публичните полета като XML елементи.

Атрибутът XmlIgnoreAttribute указва, че полето не трябва да бъде сери­а­­лизирано.

Атрибутът XmlArrayAttribute указва, че полето, към което е приложен, трябва да бъде сериализирано като масив. Чрез този атрибут може да укажем и името на генерирания XML елемент.

Атрибутът XmlArrayItem обикновено се използва заедно с атрибута XmlArrayAttribute и идентифицира тип, който може да се сериализира в масив. Чрез този атрибут също може да укажем името на генерирания XML елемент (както сме направили в нашия пример).


Контрол на XML сериализацията – пример


Ще представим още един, по-обширен, пример как чрез атрибути може да се кон­тро­лира процесът на XML сериализацията:

using System;

using System.IO;


using System.Runtime.Serialization;

using System.Xml.Serialization;


[XmlRoot("animal")]

public class Animal

{

[XmlArray("eyes")]



[XmlArrayItem("eye")]

public Eye[] Eyes;

[XmlElement("claws")]

public Claw[] Claws;

[XmlIgnore]

public string SomeMember = "Some member";


public Animal Friend;

}
public class Eye

{

[XmlAttribute("vision")]



public double Vision;
public Eye()

{

}


public Eye(double aVision)

{

Vision = aVision;



}

}
public class Claw

{

[XmlElement(ElementName="claw")]



public string Description;
public Claw()

{

}


public Claw(string aDescription)

{

Description = aDescription;



}

}
public class ControllingSerializationDemo

{

public static void SerializeAnimalToXml(Animal aAnimal,



string aFileName)

{

XmlSerializer xmlSerializer =



new XmlSerializer(typeof(Animal));

TextWriter writer = new StreamWriter(aFileName);

using (writer)

{

xmlSerializer.Serialize(writer, aAnimal);



}

}
public static Animal DeserializeAnimalFromXml(

string aFileName)

{

TextReader reader = new StreamReader(aFileName);



using (reader)

{

XmlSerializer xmlSer = new XmlSerializer(typeof(Animal));



object deserializedAnimal = xmlSer.Deserialize(reader);

return (Animal) deserializedAnimal;

}

}
public static void Main()



{

Animal animal1 = new Animal();

animal1.Eyes = new Eye[] {new Eye(1.05), new Eye(0.85)};

animal1.Claws = new Claw[] {

new Claw("Left claw"),

new Claw("Right claw")};

Animal animal2 = new Animal();

animal2.Eyes = new Eye[] {new Eye(1.00), new Eye(1.00)};

animal2.Claws = new Claw[] {new Claw("Beautiful claw")};

animal1.Friend = animal2;

// animal2.Friend = animal1;
SerializeAnimalToXml(animal1, "animal.xml");

Console.WriteLine("Animal serialized.");

Animal deserializedAnimal =

DeserializeAnimalFromXml("animal.xml");

Console.WriteLine("Animal deserialized.");

}

}


Как работи примерът?


Класът Animal съдържа няколко полета, за които сме указали чрез атри­бу­ти­те XmlArray, XmlArrayItem, XmlElement и XmlIgnore как трябва да се запишат в изходния XML.

Класовете Eye и Claw, които се използват от класа Animal също ползват атри­бути, за да опишат как да се запишат в изходния XML.

В класа ControllingSerializationDemo са реализирани два метода – SerializeAnimalToXml и DeserializeAnimalFromXml, които съответно сери­ализират и десериализират Animal обекти.

Във метода Main() създаваме две инстанции на класа Animal, задава­ме стойности на публичните им членове и правим едната инстанция член на другата. След това извършваме сериализация във файла animal.xml и десериализираме този файл, за да получим обратно записаната в него Animal инстанция. След като изпълним примера получаваме следният резултат:



Обектът бива сериализиран и след това обратно десериализиран. На картинката по-долу виждаме как изглежда и файлът animal.xml, получен при сериализацията на обекта.

Забелязва се, че полето SomeMember не е било сериализирано, понеже е мар­ки­ра­но с атрибута XmlIgnore. Имената на елементите са такива, как­вито сме указали чрез атрибутите, които сме приложили към полетата.

Ако в горния пример премахнем коментара от реда "animal2.Friend = animal1" и така направим двете инстанции на класа Animal циклично свързани една с друга и изпълним след това примера, ще получим из­клю­че­ние. Това се случва, защо­то XML сериализацията не може да сериали­зира циклични структури.




Външен контрол на XML сериализацията


В .NET Framework е предвиден механизъм, който ни позволява да контро­ли­раме XML сериализацията извън обекта, т.е. без да указваме това в из­ходния код на класа. Този механизъм се използва, когато нямаме достъп до изходния код на класа или когато искаме да създадем един набор от сериализируеми класове, но да сериализираме обектите по различен начин в зависимост от това къде се използват.

Външният контрол на сериализацията прилича много на контрола на сериализацията с атрибути. Функционалността е същата като при нея, дори класовете са същите, само механизмът за добавяне е различен.

Външният контрол на сериализацията се извършва чрез класовете XmlAttributesOverrides и XmlAttributes. Чрез тях, за всеки член на даден клас, се задава колекция XmlAttributes, описваща формата на изходния XML. За целта се създава XmlAttributesOverrides обект, който по-късно се подава на конструктора на XmlSerializer. Резултатният XmlSerializer обект използва информацията, която се съдържаща в XmlAttributesOverrides, за да определи как да извърши сериализацията. XmlAttributesOverrides обекта съдържа колекция от типове, за които ще бъде предефинирана автоматичната сериализация, както и XmlAttributes обект, асоцииран с всеки един от тях. XmlAttributes обектът съдържа избран набор от атрибути, указващи как да бъдат сериализирани всяко едно поле, свойство или клас.

Нека разгледаме следващия фрагмент код, илюстриращ как става това:



XmlAttributeOverrides overrides = new XmlAttributeOverrides();

XmlAttributes attribs = new XmlAttributes();

attribs.XmlElements.Add(new XmlElementAttribute("PersonName"));

overrides.Add(typeof(Person), "Name", attribs);

XmlSerializer xmlSerializer =

new XmlSerializer(typeof(Person), overrides);

...


В примера указваме на XML сериализацията, че полето (или свойството) Name на класа Person трябва да се запише в XML таг с име PersonName.

Първо създаваме XmlAttributesOverrides обект. След това създаваме XmlAttributes обект и към колекцията му XmlElements добавяме нов XmlElementAttribute. После, използвайки метода Add(…), добавяме XmlAttributes обекта към XmlAttributesOverrides обекта. Като пара­мет­ри на метода подаваме и типа, за който предефинираме сериализацията, както и името на полето, чиято сериализация предефинираме. Накрая подаваме XmlAttributesOverrides обекта на конструктора на XmlSerializer.


Външен контрол на сериализацията – пример


Ще представим един пример, илюстриращ, как можем да контролираме формата на изходния XML документ при XML сериализация по недекла­ра­тивен път (без да се променя сорс кода на класа, който се сериализира):

using System;

using System.IO;

using System.Xml.Serialization;
public class Person

{

public string Name;



public int Age;

public string[] Friends;

}
class OverridingXmlSerializationDemo

{

static void Main()



{

Person person = new Person();

person.Name = "Бай Мангал";

person.Age = 82;

person.Friends = new string[] {"Дядо Мраз", "Баба Яга"};
XmlAttributeOverrides overrides =

new XmlAttributeOverrides();


XmlAttributes nameAttributes = new XmlAttributes();

XmlElementAttribute nameElement =

new XmlElementAttribute("PersonName");

nameAttributes.XmlElements.Add(nameElement);

overrides.Add(typeof(Person), "Name", nameAttributes);
XmlAttributes friendsAttributes = new XmlAttributes();

XmlArrayAttribute friendsArray =

new XmlArrayAttribute("PersonFriends");

friendsAttributes.XmlArray = friendsArray;

XmlArrayItemAttribute friendsArrayItem =

new XmlArrayItemAttribute();

friendsArrayItem.ElementName = "FriendName";

friendsAttributes.XmlArrayItems.Add(friendsArrayItem);

overrides.Add(typeof(Person), "Friends",

friendsAttributes);


TextWriter writer = new StreamWriter("person.xml");

using (writer)

{

XmlSerializer xmlSer = new XmlSerializer(typeof(Person),



overrides);

xmlSer.Serialize(writer, person);

}

Console.WriteLine("Person instance serialized.");



}

}

Как работи примерът?


Дефинирали сме клас Person с няколко полета. В началото на функцията Main() създаваме инстанция на класа Person и инициализираме нейните полетата. След това на полето Name от класа Person съпоставяме колек­ция от XML атрибути, които указват, че това поле трябва да се форматира като XML елемент с име PersonName. После на полето Friends от класа Person (което представлява масив от низове) съпоставяме колекция от XML атрибути, които указват, че това поле трябва да се форматира като XML елемент с име PersonFriends, което съдържа в себе си за всеки елемент от масива по един XML елемент с име FriendName. Накрая сериализираме обекта във файла person.xml.

Ето как изглежда и файлът person.xml, полу­чен при сериализацията:



Виждаме, че полетата са сериализирани по начина, който сме указали чрез атрибутите, които сме приложили към тях.


Приложение: FormatterServices


Ще разгледаме съвсем накратко, без да даваме пример, средствата за реализация на собствени форматери в .NET Framework. Едно от тези средства е класът FormatterServices. Той предоставя основ­ната функ­ционалност, която трябва да притежава форматера – извличане на сериа­лизируемите членове на обект, определяне на техните типове и извличане на стойностите им. Този клас не може да бъде наследяван.

Методи за сериализация

public static MemberInfo[] GetSerializableMembers(Type)


Методът приема като параметър типа на класа, който ще бъде сериали­зиран, и връща като резултат масив от MemberInfo обекти, съдържащи информация за сериализируемите членове на класа.

public static Object[] GetObjectData(Object, MemberInfo[])


Методът приема като параметри обект, който ще бъде сериализиран и масив с членовете, които трябва бъдат извлечени от обекта. За всеки от тях се извлича стойността, асоциирана с него в сериализирания обект и тези стойности се връщат като масив от обекти. Дължината му е същата, като дължината на масива с членовете, извличани от обекта.

Методи за десериализация

public static Type GetTypeFromAssembly(Assembly, String)


Методът намира типа на определен обект в дадено асембли. Той приема като параметри асемблито и името на обекта, който ще се търси, и връща като резултат типа на този обект.

public static Object GetUninitializedObject(Type)


Методът приема като параметър тип на обект и връща като резултат нова инстанция на обект от дадения тип.

public static Object GetObjectMembers(Object, MemberInfo[], Object[])


Методът попълва със стойности полетата на обект, като тези стойности се вземат от масив с обекти. За целта като параметри му се подават обекта, чиито полета ще се запълват, масив от MemberInfo обекти, описващ кои полета да се запълват и масив с обекти, от който ще се вземат стой­ностите за полета. Като резултат се връща обекта с попълнени поле­та.

Упражнения


  1. Да се дефинира клас Graph, който описва насочен граф (представен като масив от върхове). Да се дефинира клас Node, който описва един връх от графа. Класът Node трябва да съдържа информационна част (текстово поле) и масив от наследници (инстанции на същия клас Node). Да се Реализира функционалност, която сериализира и десери­али­зи­ра инстанции на класа Graph.

  2. Опитайте се да сериализирате бинарно инстанция на класа System. Collections.Hashtable. Опитайте след това да сериализирате хеш-таблица с XML сериализация. Какви проблеми възникват? Можете ли да обясните защо XML сериализацията не работи? Предложете алтер­нативно решение.

  3. Дефинирайте класове Country и Town, които съдържат информация за държави и градове. Може да считате, че в една държава има много градове. Реализирайте бинарна и XML сериализация и десериализация за тези класове. Реализирайте TCP сървър, който по име на държава връща информация за държавата заедно с всички градове в нея (във вид на бинарно сериализиран Country обект). Реализирайте Windows Forms клиентско приложение за TCP сървъра, което позволява да се извлича и визуализира информация за държавите. Клиентът и сървъ­рът трябва да поддържат два режима на работа – с бинарна сериали­зация и с XML сериализация.

  4. Обяснете защо SoapFormatter може да сериализира цикличен граф от обекти, а XML сериализацията не може. Упътване: създайте цикличен граф от обекти, сериализайте го по двата начина и сравнете изходните XML файлове.

Използвана литература


  1. Михаил Стойнов, Сериализация на данни – http://www.nakov.com/ dotnet/lectures/Lecture-19-Serialization-v1.0.ppt

  2. MSDN Library – http://msdn.microsoft.com

  • Object Serialization in the .NET Framework

  • System.Runtime.Serialization Namespace

  • System.Runtime.Serialization.Formatters Namespace

  • System.Runtime.Serialization.Formatters.Binary Namespace

  • System.Runtime.Serialization.Formatters.Soap Namespace

  • System.Xml.Serialization Namespace

  • XML and SOAP Serialization

  • XmlSerializer Class

  • Controlling XML Serialization Using Attributes

  • Overriding XML Serialization

  • Attributes That Control Encoded SOAP Serialization

  • Attributes That Control XML Serialization

  • The XML Schema Definition Tool and XML Serialization

  • Generating SOAP Messages With XML Serialization

  • FormatterServices Class

  1. Vyacheslav Biktagirov, .NET Serialization – http://www.csharphelp.com /archives/archive38.html

  2. Mickey Williams, CodeGuru: .NET Serialization - http://www.codeguru. com/columns/DotNet/article.php/c6595/



Каталог: dotnetcourse -> Beta1
Beta1 -> Изграждане на графичен потребителски интерфейс с Windows Forms
Beta1 -> Асемблита и разпространение
Beta1 -> Управление на паметта и ресурсите
Beta1 -> Уеб услуги с asp. Net
dotnetcourse -> Кратко ръководство и полезни съвети за това как да разработим своя практически проект
dotnetcourse -> Въпрос: Кога е нужно да слагам в класовете си свойства, и кога мога да слагам публични полета? Не е ли вторият вариант по-удобен, особено когато едно поле не се валидира по никакъв начин? Отговор
Beta1 -> Отдалечени извиквания с. Net remoting


Сподели с приятели:
1   2




©obuch.info 2024
отнасят до администрацията

    Начална страница