среда, ноября 01, 2006

Xml Serialization

Xml сериализация в .Net Framework 2.0 tips & tricks



Вступление


В данной статье рассматриваются различные практические аспекты использования Xml сериализации в .Net Framework. В статье мало текста, зато много примеров кода и Xml.


Что такое Xml сериализация.


Xml широко используется в .Net приложениях, и .Net framework предоставляет богатые возможности по работе с Xml. Среди них: поддержка Xml DOM (System.Xml.XmlDocument), последовательное чтение - запись Xml, поддержка xPath и xQuery, поддержка XSLT, богатые возможности DataSet по работе с Xml и, наконец, Xml сериализация.


Xml сериализация позволяет представлять Xml в виде иерархии классов, и наоборот, данные классов представлять в виде Xml. Когда стоит использовать Xml сериализацию? Мое мнение таково. Во всех случаях, когда нам заранее известна структура Xml, с которым предстоит работать, следует использовать Xml сериализацию.


Как работает Xml сериализация.


При Xml сериализации, сериализуются все открытые поля и свойства класса. Кроме того, открытые свойства должны иметь аксессоры get и set, а сам класс должен иметь конструктор по умолчанию без параметров. Рассмотрим класс:




public class DataClass
{
public DataClass(){}
public string ID = Guid.NewGuid().ToString();
public string Name = "Just Name";
public Decimal Count = 10;
public DateTime Date = DateTime.Now;
}


Его мы будем использовать во всех дальнейших примерах сериализации и десериализации.


Для сериализации нам надо создать экземпляр класса XmlSerializer, предав в качестве параметра конструктора тип сериализуемого класса. Следующий код демонстрирует сериализацию объекта нашего класса DataClass:



DataClass obj = new DataClass();
// создаем сериалайзер
XmlSerializer sr = new XmlSerializer(obj.GetType());
// создаем writer, в который будет происходить сериализация
StringBuilder sb = new StringBuilder();
StringWriter w = new StringWriter(sb, System.Globalization.CultureInfo.InvariantCulture);
// сериализуем
sr.Serialize(w,obj);
// получаем строку Xml
string xml = sb.ToString();
Console.WriteLine(xml);


В результате получаем вот такой Xml:



<?xml version="1.0" encoding="utf-8"?>
<DataClass xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<ID>34332413-e70f-44f7-b35c-b34c47812dbc</ID>
<Name>Just Name</Name>
<Count>10</Count>
<Date>2006-10-30T12:35:20.3110319+03:00</Date>
</DataClass>


Для того, чтобы получить из этого Xml экземпляр класса DataClass (десериализовать), служит следующий код:



// создаем reader
StringReader reader = new StringReader(xml);
// создаем XmlSerializer
XmlSerializer dsr = new XmlSerializer(typeof(DataClass));
// десериализуем
DataClass clone = (DataClass)dsr.Deserialize(reader);


Управлять, тем как свойства и поля объекта отображаются на элементы Xml, можно при помощи атрибутов, которые содержатся в пространстве имен System.Xml.Serialization. Допустим, мы хотим, чтобы в результирующем Xml корневой элемент назывался <Data>, поля “ID”, “Name” сериализовались Xml атрибутами, поле “Count” соответствовало Xml элементу <Reserved>, а поле “Date”, вообще не попадало в результирующий Xml. Для этого расставим соответствующие атрибуты:



    [XmlRoot("Data")] // изменим имя корневого элемента
public class DataClass
{
public DataClass(){}

[XmlAttribute] // сериализуем в xml атрибут
public string ID = Guid.NewGuid().ToString();

[XmlAttribute] // сериализуем в xml атрибут
public string Name = "Just Name";

[XmlElement("Reserved")] // изменим имя xml элемента
public Decimal Count = 10;

[XmlIgnore] // не будет сериализоваться
public DateTime Date = DateTime.Now;
}


Результирующий Xml теперь выглядит так:





<?xml version="1.0" encoding="utf-8"?>
<Data xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" ID="cc340d5e-4c80-4fe5-9c3d-e49f64e26b22" Name="Just Name">
<Reserved>10</Reserved>
</Data>



Как видно из представленного кода, использовать Xml сериализацию не сложно. Но все же попробуем облегчить себе жизнь. Для этого вынесем код, ответственный за сериализацию в отдельный класс утилиту:



public class XmlUtility
{
/// <summary>
/// Сериализует объект в строку XML
/// </summary>
public static string Obj2XmlStr(object obj, string nameSpace)
{
if (obj == null) return string.Empty;
XmlSerializer sr = new XmlSerializer(type);
StringBuilder sb = new StringBuilder();
StringWriter w = new StringWriter(sb, System.Globalization.CultureInfo.InvariantCulture);
sr.Serialize(
w,
obj,
new XmlSerializerNamespaces(
new XmlQualifiedName[]
{
new XmlQualifiedName("", nameSpace)
}
));
return sb.ToString();
}

/// <summary>
/// Сериализует объект в строку XML
/// </summary>
public static string Obj2XmlStr(object obj)
{
if (obj == null) return string.Empty;
XmlSerializer sr = new XmlSerializer(obj.GetType());
StringBuilder sb = new StringBuilder();
StringWriter w = new StringWriter(sb, System.Globalization.CultureInfo.InvariantCulture);
sr.Serialize(
w,
obj,
new XmlSerializerNamespaces( new XmlQualifiedName[] { new XmlQualifiedName(string.Empty) } ) );
return sb.ToString();
}

/// <summary>
/// Десериализует строку XML в объект заданного типа
/// </summary>
/// <param name="xml"></param>
/// <param name="type"></param>
/// <returns></returns>
public static T XmlStr2Obj<T>(string xml)
{
if (xml == null) return default(T);
if (xml == string.Empty) return (T)Activator.CreateInstance(typeof(T));

StringReader reader = new StringReader(xml);
XmlSerializer sr = new XmlSerializer(typeof(T));//SerializerCache.GetSerializer(type);
return (T)sr.Deserialize(reader);
}
}


Назначение метода Obj2XmlStr(object obj, string nameSpace) я объясню несколько позже. Теперь наш код для сериализации и десериализации объекта сократился в несколько раз:



DataClass obj = new DataClass();
string xml = XmlUtility.Obj2XmlStr(obj);
Console.WriteLine(xml);
DataClass clone = XmlUtility.XmlStr2Obj<DataClass>(xml);


Полный код класса XmlUtility вы найдете в исходном коде, прилагаемом к статье.


Namespce и сериализация.


Когда мы используем для сериализации метод XmlUtility.Obj2XmlStr(obj), мы можем заметить, что результирующий xml получается несколько более компактным чем прежде потому, что из него исчезли стандартные префиксы нэймспейсов XmlSchema - xsd и XmlSchema Instance - xsi :



<?xml version="1.0" encoding="utf-8"?>
<Data ID="078ce191-f781-4052-93fd-766309c2abaa" Name="Just Name">
<Reserved>10</Reserved>
</Data>


Произошло это из-за использования перегруженного метода SmlSerializer.Serialize(), в котором явно указывается перечень используемых Xml namespace – ов (пустой в данном конкретном случае).


Есть в нашей утилите и метод, позволяющий явно задать namespace для сериализации - Obj2XmlStr(object obj, string nameSpace). Учет Xml namespace при сериализации очень важен. Рассмотрим следующий пример:



DataClass obj = new DataClass();
// зададим явно XmlNamespace при сериализации
string xml = XmlUtility.Obj2XmlStr(obj, "urn:MyDataClass");
Console.WriteLine(xml);
DataClass clone = XmlUtility.XmlStr2Obj<DataClass>(xml);


При попытке десериализации полученного Xml обратно в объект, в пятой строке мы получим исключение:



<?xml version="1.0" encoding="utf-8"?>
<Data xmlns="urn:MyDataClass" ID="e46917f4-691a-4a7f-9f0b-d9be5e018a1f" Name="Just Name">
<Reserved>10</Reserved>
</Data>

System.InvalidOperationException : <Data xmlns='urn:MyDataClass'> was not expected.


Дело в том, что сериалайзеру не было никаких указаний по поводу Xml namespace для данного класса. И появление объявления namespace по умолчанию xmlns="urn:MyDataClass" становится непреодолимой преградой на пути десериализации Xml в объект. Явно указать, какой namespace надо использовать сериалайзеру можно в атрибуте XmlRootAttribute. Например:



[XmlRoot("Data", Namespace=DataClass.XmlNamespace )]
public class DataClass
{
public const string XmlNamespace = "urn:MyDataClass";


После этих изменений в коде, ошибка времени выполнения при десериализации пропадает и код, приведенный ранее, выполняется.


А что будет, если теперь при сериализации не указывать namespace явно, а вызвать



string xml = XmlUtility.Obj2XmlStr(obj);


Результирующий Xml опять изменится, но сериализация и десериализация выполнятся без ошибок:



<?xml version="1.0" encoding="utf-8"?>
<q1:Data ID="1458c25e-4356-4df5-b918-a44c9bf5e4b0" Name="Just Name" xmlns:q1="urn:MyDataClass">
<q1:Reserved>10</q1:Reserved>
</q1:Data>


Мы видим, что все xml элементы получили префикс “ql”. Произошло это из-за того, что namespace "urn:MyDataClass" объявлен в атрибуте XmlRoot нашего класса, а при сериализации он не был задан в качестве namespace по умолчанию. Таких ситуаций следует избегать. Лучше, либо вообще не использовать namespace, либо задавать их явно в качестве namespace по умолчанию, что позволяет избежать использования префиксов в Xml. Nmespace можно задать не только для класса, но и для каждого элемента в отдельности (но без нужды лучше этого не делать). Кроме того, при разработке крупных систем, может оказаться, что для разных классов заданы разные namespace, и тут уже не избежать появления префиксов имен в результирующем Xml. Поэтому данному вопросу необходимо постоянно уделять внимание.


Сериализация массивов и коллекций.


Рассмотрим некоторые особенности сериализации массивов и коллекций. Для этого добавим в объявление нашего сериализуемого класса поле в виде массива строк:



[XmlRoot("Data", Namespace=DataClass.XmlNamespace )]
public class DataClass
{
...
//массив строк
public string[] Lines = new string[] { "Line one", "Line two", "Line three" };
}


После сериализации мы получим следующий Xml



<?xml version="1.0" encoding="utf-8"?>
<Data ID="cafaafbf-fa51-47d5-a921-0ffeb8a7e345" Name="Just Name" xmlns="urn:MyDataClass">
<Reserved>10</Reserved>
<Lines>
<string>Line one</string>
<string>Line two</string>
<string>Line three</string>
</Lines>
</Data>


По умолчанию, массив (коллекция) сериализуется в xml элемент с именем поля или свойства, в который вложены элементы, входящие в массив. Возможно, это не совсем тот Xml который вы хотели бы увидеть. Управлять сериализацией массива помогают атрибуты XmlArray, XmlArrayItem, и XmlElement. Давайте рассмотрим эти возможности подробнее. Меня, например, не устраивает то, что элементы массива называются <string>. Мне нужно, чтобы элементы назывались <Line>. Достигается это при помощи атрибута XmlArrayItem над полем Lines. Если же нам надо изменить название элемента <Lines>, то для этого используем атрибут XmlArray:



[XmlArray("Specification")]
[XmlArrayItem("Line")]
public string[] Lines = new string[] { "Line one", "Line two", "Line three" };


Результирующий Xml изменился:



<?xml version="1.0" encoding="utf-8"?>
<Data ID="64a2c310-fa58-4a28-9cb2-7652625b28fc" Name="Just Name" xmlns="urn:MyDataClass">
<Reserved>10</Reserved>
<Specification>
<Line>Line one</Line>
<Line>Line two</Line>
<Line>Line three</Line>
</Specification>
</Data>


Ну, и, наконец, часто бывают ситуации, когда мы хотели бы вообще избавиться от элемента <Specification> и получить вот такой xml:



<?xml version="1.0" encoding="utf-8"?>
<Data ID="58f7271e-d6b2-4c7f-b3f3-334d8f087f09" Name="Just Name" xmlns="urn:MyDataClass">
<Reserved>10</Reserved>
<Line>Line one</Line>
<Line>Line two</Line>
<Line>Line three</Line>
</Data>


Делается это достаточно просто. Вместо атрибутов XmlArray и XmlArrayItem следует явно задать атрибут XmlElement над полем или свойством типа массива или коллекции. Для получения приведенного выше Xml, используем следующий код:



[XmlElement("Line")]
public string[] Lines = new string[] { "Line one", "Line two", "Line three" };


Подобно массивам сериализуются коллекции. Например мы можем добавить в объявление DataClass поле типа ArrayList и заполнить его различными значениями:



public class DataClass
{
...
// коллекция
public ArrayList List = new ArrayList();
}

// создаем экземпляр и наполняем коллекцию List различными объектами
DataClass obj = new DataClass();
obj.List.Add(new DataClass());
obj.List.Add("This is a string");
string xml = XmlUtility.Obj2XmlStr(obj, DataClass.XmlNamespace);
Console.WriteLine(xml);


Сериалайзер справился с поставленной задачей и выдал вот такой xml:



<?xml version="1.0" encoding="utf-8"?>
<Data ID="b1d70646-0985-424a-b87b-1e910173c9e5" Name="Just Name" xmlns="urn:MyDataClass">
<Reserved>10</Reserved>
<Line>Line one</Line>
<Line>Line two</Line>
<Line>Line three</Line>
<List>
<anyType d3p1:type="DataClass" ID="0a57f292-7a0e-479d-93e2-6c42fa019025" Name="Just Name" xmlns:d3p1="http://www.w3.org/2001/XMLSchema-instance">
<Reserved>10</Reserved>
<Line>Line one</Line>
<Line>Line two</Line>
<Line>Line three</Line>
<List />
</anyType>
<anyType xmlns:q1="http://www.w3.org/2001/XMLSchema" d3p1:type="q1:string" xmlns:d3p1="http://www.w3.org/2001/XMLSchema-instance">This is a string</anyType>
</List>
</Data>


Элементы списка сериализуются в xml элементы <anyType>, в которые добавляется дополнительная информация о типе каждого элемента. Однако, подобная сериализация, это не самая лучшая идея, поверьте. Особенно если данный Xml предназначен для обмена данными с другой системой. Дело в том, что в нашем случае схема результирующего Xml расширяется в runtime в зависимости от типа элементов, добавляемых в коллекцию. Далеко не все системы смогут правильно обработать такой Xml. По возможности, следует избегать использования не типизированных коллекций. Совсем другое дело - использование Generic коллекций. Модифицируем наш DataClass следующим образом, вместо ArrayList используем List<DataClass>:



public class DataClass
{
...
//public ArrayList List = new ArrayList();
public List<DataClass> List = new List<DataClass>();
}


Результирующий Xml выглядит теперь вот так:



<?xml version="1.0" encoding="utf-8"?>
<Data ID="13688ff2-c55f-45d7-8498-750a3e5df1d1" Name="Just Name" xmlns="urn:MyDataClass">
<Reserved>10</Reserved>
<Line>Line one</Line>
<Line>Line two</Line>
<Line>Line three</Line>
<List>
<DataClass ID="c250a091-4d99-40eb-a870-201f6130579b" Name="Just Name">
<Reserved>10</Reserved>
<Line>Line one</Line>
<Line>Line two</Line>
<Line>Line three</Line>
<List />
</DataClass>
</List>
</Data>


Как мы можем убедиться из xml исчезли элементы <anyType>, потому что тип элементов коллекции известен заранее.


Xml Сериализация и наследование


У Xml сериализации непростые отношения с объектным наследованием. Рассмотрим пример. Объявим класс ChildClass – наследник DataClass, а в самом DataClass объявим поле Child:



public class DataClass
{
...
[XmlElement]
public DataClass Child;
...
}

public class ChildClass : DataClass
{
public string ParentName;
}


А теперь попробуем воспользоваться полиморфизмом, и присвоим DataClass.Child объект типа ChildClass



DataClass obj = new DataClass();
obj.Child = new ChildClass();
string xml = XmlUtility.Obj2XmlStr(obj, DataClass.XmlNamespace);
Console.WriteLine(xml);


Результат будет плачевный. XmlSerializer не приветствует наши опыты с полиморфизмом и отказывается сериализовать наши объекты, выдавая при этом исключение:



UnitTest.XmlUtilityTest.SerializeTest : System.InvalidOperationException : There was an error generating the XML document.
----> System.InvalidOperationException : The type UnitTest.ChildClass was not expected. Use the XmlInclude or SoapInclude attribute to specify types that are not known statically.


В тексте сообщения об ошибке содержатся довольно четкие инструкции о том, как исправить ситуацию. Необходимо использовать атрибут XmlInclude с указанием конкретного типа - наследника. Им мы должны пометить базовый класс. Вот таким образом:



[XmlRoot("Data", Namespace=DataClass.XmlNamespace )]
[XmlInclude(typeof(ChildClass))]
public class DataClass
{
public const string XmlNamespace = "urn:MyDataClass";
public DataClass(){}

[XmlElement]
public DataClass Child;
...
}
[XmlType(Namespace=DataClass.XmlNamespace)]
public class ChildClass : DataClass
{
public string ParentName;
}


Причем при появлении новых наследников, мы должны добавлять новые XmlInclude над базовым классом. Если же исходный код базового класса нам не доступен, то мы попадаем в довольно сложную ситуацию. Предположим мы не можем изменять описание базового класса DataClass, потому что у нас нет доступа к его исходному коду. У нас объявлен еще один его наследник CrandChildClass:DataClass, и мы присваиваем полю DataClass.Child экземпляр этого нового типа. Сериализация в этом случае не возможна. Но выход, все-таки есть, хотя и довольно запутанный. Специально для этого случая существует тип XmlAttributeOverrides и конструктор в XmlSerializer, принимающий этот тип. Суть его использования состоит в том, что с его помощью мы в runtime можем переопределить атрибуты, управляющие xml сериализацией, заданные в исходном коде.


Вот пример:



DataClass obj = new DataClass();
// присваиваем значение наследника для которого не был определен XmlInclude
obj.Child = new GrandChildClass();
// переопределяем атрибуты сериализации
XmlAttributes attrs = new XmlAttributes();
XmlElementAttribute attr = new XmlElementAttribute();
attr.ElementName = "GrandChildClass";
attr.Type = typeof(GrandChildClass);
attrs.XmlElements.Add(attr);
XmlAttributeOverrides attrOverrides = new XmlAttributeOverrides();
attrOverrides.Add(typeof(DataClass), "Child", attrs);
// используем специальный конструктор
XmlSerializer sr = new XmlSerializer(typeof(DataClass), attrOverrides);
StringBuilder sb = new StringBuilder();
StringWriter w = new StringWriter(sb, System.Globalization.CultureInfo.InvariantCulture);
sr.Serialize(w,obj); // успех
string xml = sb.ToString();
Console.WriteLine(xml);


Видите, сколько кода пришлось написать, только для того чтобы сделать возможным сериализацию вот этой строки: obj.Child = new GrandChildClass(); Причем надо учитывать, что это всего лишь пример. В этом коде мы не сможем сериализовать obj если присвоим obj.Child значение любого другого типа кроме GrandChildClass. Для этого нам надо будет добавить в XmlAttributeOverrides переопределения атрибутов “Child” для всех остальных типов, которые предполагается использовать.




Вопросы производительности.


Cоздание экземпляра класса XmlSerializer довольно дорогая операция. Дело в том, что для переданного в конструктор типа, динамически создаются и компилируются сборки, содержащие код, предназначенный для сериализации именно этого типа. В результате собственно Xml сериализация выполняется довольно быстро, а вот создание экземпляров XmlSerializer занимает очень много времени. Кроме того, созданные сборки не выгружаются в результате возникает утечка памяти. В Framework 1.0 все было совсем плохо, и поэтому в одном из приложений, которое активно использовало Xml сериализацию, я создал небольшой класс, который кэширует экземпляры XmlSerializer, используя в качестве ключа полное имя типа класса подлежащего сериализации:



/// <summary>
/// Кэш для используемых сериалайзеров
/// </summary>
internal class SerializerCache
{
private static Hashtable hash = new Hashtable();
public static XmlSerializer GetSerializer(Type type)
{
XmlSerializer res = null;
lock(hash)
{
res = hash[type.FullName] as XmlSerializer;
if(res == null)
{
res = new XmlSerializer(type);
hash[type.FullName] = res;
}
}
return res;
}
}


Этот маленький класс сотворил чудо. Везде где мне нужен экземпляр XmlSerializer, вместо конструктора я использую SerializerCache.GetSerializer(). В результате производительность операций xml сериализации выросла на порядок.


Во Framework 2.0 разработчики отчасти поправили положение. Теперь XmlSerializer сам кэширует создаваемые им сборки, правда происходит это только при использовании конструкторов System.Xml.Serialization.XmlSerializer(Type) System.Xml.Serialization.XmlSerializer(Type,String).


Я решил проверить, имеет ли теперь смысл использовать предложенный мной класс SerializerCache? Для этого выполним тест. Сначала сериализуем 1000 объектов DataClass создавая каждый раз новый экземпляр XmlSerialize. А затем сериализуем 1000 объектов DataClass используя SerializerCache (т.е. используя один экземпляр XmlSerializer). Измерим время, затраченное на два теста:



Direct serialization time 00:00:02.2574448
Cache serialization time 00:00:00.3135340


Как видно, кэширование экземпляров XmlSerializer по прежнему дает почти семикратный выигрыш во времени исполнения. Поэтому я рекомендую использовать эту технику, если ваше приложение активно использует Xml сериализацию.




Исходный код к статье

14 комментариев:

Slava Drozd комментирует...

Посмотрел, что автору никто не оставляет комментариев, и решил оставить.
Хорошо написано, просто в общем-то и понятно.
Так держать, одним словом!

Правда я вот попробовал повторить этот т.н. Walkthrough и... видимо из-за различия версий VS2005 результаты не совсем одинаковые.

1. Не получилось добиться ругательств от VS (Exceptions, etc.)

2. Где-то в коде в конструкторе аттрибута указано что-то типа DateClass.XmlNamespace - моя VS на это ругается, и ваша, думаю, тоже, - хотя это и не принципиально конечно.

3. Где-то в коде в exceptions проскочило UnitTest если не ошибаюсь, и совсем не понятно вообще при чём тут UnitTest...

А вообще всё хорошо, писать и писать...

Кстати, если вдруг интересно, на сайт я набрёл отсюда:
http://www.rusdoc.ru/reviews/programming/pl/dotnet/

Ах да, чуть не забыл...
Мне кажется, что статья получилась бы интереснее, если бы вы сюда добавили случаи практического применения сериализации объектов.
Что-то типа - "была вот такая вот задачка и вот с помощью сериализации она решается просто замечательно", но это так...

Респект!

Sergey Rozovik комментирует...

Спасибо Слава. Там в конце статьи есть линк на архив с кодом solution VS2005. Я думаю он снимет все вопросы.

Icq комментирует...

Привет, очень понравился твой код, красиво!!!

Но вот у меня одно не получилось, каким образом я могу выставить аттрибуты у CHILDNODE

Анонимный комментирует...

Для повышения производительности в методе SerializerCache. GetSerializer лучше использовать lock с двойной проверкой

linker комментирует...

Вот почитал статейку, вроде все ни чего, только где-то я уже это видел.Сходил до полки книжками и точно почтислово в слово.
С# для профессионалов, том второй,глава тринадцатая.;)

Sergey Rozovik комментирует...

to linker
Не знаю "С# для профессионалов, том второй,глава тринадцатая" не читал :) Авторы то хоть кто?
А статейка действительно разошлась по рунету широко. Причем многие уважаемые сайты даже авторство не указывают.
А код к статейке мой родной, написан еще в году этак 2002 под .Net 1.0, а потом портирован под 2.0. Неужели в "С# для профессионалов" и код мой потырили? ;)

linker комментирует...

Книжка вот http://www.rsdn.ru/res/book/net/csharp_pro.xml
Но я дико извиняюсь, был не прав, это у меня глаз замылился, в книге другой класс,но примеры похожи, но это и не мудрено.Просто пока разбирался с сериализацией наткнулся видно где-то еще на похожую статью.
Так что приношу свои извинения.

Анонимный комментирует...

Сергей, статья интересная, и мне лично она очень помогла за что Вам большое человеческо-программистское спасибо. Но возникла такая проблема...

У меня есть примерно следующие классы:

class Configuration
{
public ModuleList {get; set;}
}

class Module
{
public string Name {get; set;}
public ModuleList ModuleList {get; set}
}

class ModuleList
{
// угловые скобки заменены на квадратные
public SortedList[string, Module] SubmoduleList {get; set;}
}

При сериализации класса Configuration я воспользовался рекомендациями данной статьи в отношении namespace'ов. Но сработали Ваши рекомендации только до второго уровня вложенности - ниже при сериализации были добавлены свои namespace'ы. Результат сериализации выглядит примерно так (угловые скобки заменены на квадратные):

[?xml version="1.0" encoding="utf-8"?]
[Configuration xmlns="urn:MyApplicationConfiguration"]
[moduleList]
[module name="TestModule" enabled="true" isSystem="true" typeName="TestModule" fileName="TestModule.cfg"]
[moduleList]
[module d5p1:type="Module" name="TestModule1" enabled="false" isSystem="true" typeName="TestModule" fileName="TestModule1" xmlns:d5p1="http://www.w3.org/2001/XMLSchema-instance"]
[moduleList /]
[/module]
[module d5p1:type="Module" name="TestModule2" enabled="false" isSystem="true" typeName="TestModule" fileName="TestModule2" xmlns:d5p1="http://www.w3.org/2001/XMLSchema-instance"]
[moduleList /]
[/module]
[/moduleList]
[/module]
[module name="TestModule1" enabled="false" isSystem="true" typeName="TestModule" fileName="TestModule1"]
[moduleList /]
[/module]
[/moduleList]
[/Configuration]

Т.е. появились, выделенные мною жирным шрифтом, элементы, которые мне совсем не нужны. Можно ли это как-то побороть?

Sergey Rozovik комментирует...

Приведенная вами конструкция вообще не сериализуется. Видимо где-то ошибка. Вышлите свой код на srozovik(собака)gmail.com посмотрим что можно сделать...

Анонимный комментирует...

Во! Как раз сериализацию надо было делать. Спасибо!

Анонимный комментирует...

А что объясняют комментарии данного типа? Объясните плиз построчно. Не именно для вашего кода, а вообще.
Угловае скобки заменены на квадратные.

/// [summary]
/// Десериализует строку XML в объект заданного типа
/// [/summary]
/// [param name="xml"][/param]
/// [param name="type"][/param]
/// [returns][/returns]

Sergey Rozovik комментирует...

Коментарии с тремя слэшами /// - это стандартные коментарии C# для автодокументирования кода. Их формат описан в стандарте и в MSDN.

Анонимный комментирует...

Какая у вас зар. плата?

Sergey Rozovik комментирует...

> Какая у вас зар. плата?
Да ничего, так. Не жалуюсь.