Статьи

Общие словари против XmlSerializer

Одной из самых интересных новых возможностей .NET 2.0 является концепция Generics. И одно из наиболее распространенных применений этой функции — в новых GenericCollections. Любой, кто потратил качественное время на расширение CollectionBase и DictionaryBase в версии 1.1 (или просто обманывает, используя коллекцию поколения CodeSmith), поймет, насколько полезны эти коллекции.

Еще одна очень удобная функция .NET — встроенная, почти автоматическая XmlSerialization. Предполагая, что кто-то следует нескольким простым правилам — главным образом, имея пустой конструктор по умолчанию и следя за тем, чтобы все свойства реализовывали методы доступа Get и Set — можно мгновенно сериализовать любой объект в Xml. К сожалению, эта тактика разваливается, когда кто-то пытается использовать новый универсальный словарь в качестве члена объекта, который желает XmlSerialize. Вместо небольшого количества xml вы получите приятное небольшое исключение, в котором говорится: «Произошла ошибка, отражающая тип« ваше имя типа здесь »».

Есть несколько способов обойти эту проблему. Большинство из них включают реализацию IXmlSerializable, что приводит к написанию большого количества повторяющегося кода. И эти методы являются хорошим вариантом, если вы пишете и читаете из очень конкретной схемы. Тем не менее, часто нужно просто сериализовать что-то быстро и грязно, но работать, скажем, с какой-нибудь внутренней веб-службой или каким-то временным хранилищем.

В этом случае можно использовать следующую технику:

  1. Сначала украсьте свойство Dictionary <TKey, TValue> атрибутом XmlIgnore. Это говорит XmlSerializer игнорировать это свойство, устраняя ошибку отражения.
  2. Создайте новое открытое свойство, которое принимает и возвращает массив объектов DictionaryEntry. Я склонен называть их специально (_x_DictionaryName), чтобы было ясно, что вообще не следует использовать это свойство.
  3. Сериализировать прочь

В случае, если вышеупомянутое немного тупо, см. Пример ниже:

using System; using System.Collections; using System.Collections.Generic; using System.Text; using System.Xml.Serialization; namespace SPBlog.DictionarySerialization { [XmlRoot("StuffContainer")] public class Serializable { public Serializable() { } private Dictionary<string, int> stuff=new Dictionary<string,int>(); /// <summary> /// Public stuff dictionary. /// </summary> /// <remarks> /// Note the XmlIgnore attribute. /// </remarks> [XmlIgnore()] public Dictionary<string, int> Stuff { set { stuff = value; } get { return stuff; } } /// <summary> /// Property created expressly for the XmlSerializer /// </summary> /// <remarks> /// Note the XML Serialiazation attributes; they control what elements are named when this object is serialized. /// </remarks> [XmlArray("Stuff")] [XmlArrayItem("StuffLine", Type=typeof(DictionaryEntry))] public DictionaryEntry[] _x_Stuff { get { //Make an array of DictionaryEntries to return DictionaryEntry[] ret=new DictionaryEntry[Stuff.Count]; int i=0; DictionaryEntry de; //Iterate through Stuff to load items into the array. foreach (KeyValuePair<string, int> stuffLine in Stuff) { de = new DictionaryEntry(); de.Key = stuffLine.Key; de.Value = stuffLine.Value; ret[i]=de; i++; } return ret; } set { Stuff.Clear(); for (int i=0; i<value.Length; i++) { Stuff.Add((string)value[i].Key, (int)value[i].Value); } } } } } 

Который создает следующий Xml:

<?xml version="1.0" encoding="utf-8"?> <StuffContainer xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"> <Stuff> <StuffLine> <Key xsi:type="xsd:string">One</Key> <Value xsi:type="xsd:int">1</Value> </StuffLine> <StuffLine> <Key xsi:type="xsd:string">Two</Key> <Value xsi:type="xsd:int">2</Value> </StuffLine> <StuffLine> <Key xsi:type="xsd:string">Three</Key> <Value xsi:type="xsd:int">3</Value> </StuffLine> <StuffLine> <Key xsi:type="xsd:string">Four</Key> <Value xsi:type="xsd:int">4</Value> </StuffLine> </Stuff> </StuffContainer>
<?xml version="1.0" encoding="utf-8"?> <StuffContainer xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"> <Stuff> <StuffLine> <Key xsi:type="xsd:string">One</Key> <Value xsi:type="xsd:int">1</Value> </StuffLine> <StuffLine> <Key xsi:type="xsd:string">Two</Key> <Value xsi:type="xsd:int">2</Value> </StuffLine> <StuffLine> <Key xsi:type="xsd:string">Three</Key> <Value xsi:type="xsd:int">3</Value> </StuffLine> <StuffLine> <Key xsi:type="xsd:string">Four</Key> <Value xsi:type="xsd:int">4</Value> </StuffLine> </Stuff> </StuffContainer> 

Теперь, как вы можете видеть выше, DictionaryEntry десериализуется интересным способом — он добавляет атрибут типа к узлам Key и Value. Можно легко изменить эту технику, чтобы использовать пользовательский объект, который сериализуется для определенных типов, если требуется немного более ограниченный контроль над выводом.