XML-сериализация.NET получил?

Я столкнулся с несколькими проблемами при выполнении C# XML-сериализации, которые, как я думал, я поделюсь:

  • Вы не можете сериализовать элементы, доступные только для чтения (например, KeyValuePairs)
  • Вы не можете сериализовать общий словарь. Вместо этого попробуйте этот класс-оболочку (из http://weblogs.asp.net/pwelter34/archive/2006/05/03/444961.aspx):

using System;
using System.Collections.Generic;
using System.Text;
using System.Xml.Serialization;

[XmlRoot("dictionary")]
public class SerializableDictionary<TKey, TValue> : Dictionary<TKey, TValue>, IXmlSerializable
{      
    public System.Xml.Schema.XmlSchema GetSchema()
    {
        return null;
    }

    public void ReadXml(System.Xml.XmlReader reader)
    {
        XmlSerializer keySerializer = new XmlSerializer(typeof(TKey));
        XmlSerializer valueSerializer = new XmlSerializer(typeof(TValue));

        bool wasEmpty = reader.IsEmptyElement;
        reader.Read();

        if (wasEmpty)
            return;

        while (reader.NodeType != System.Xml.XmlNodeType.EndElement)
        {
            reader.ReadStartElement("item");

            reader.ReadStartElement("key");
            TKey key = (TKey)keySerializer.Deserialize(reader);
            reader.ReadEndElement();

            reader.ReadStartElement("value");
            TValue value = (TValue)valueSerializer.Deserialize(reader);
            reader.ReadEndElement();

            this.Add(key, value);

            reader.ReadEndElement();
            reader.MoveToContent();
        }
        reader.ReadEndElement();
    }

    public void WriteXml(System.Xml.XmlWriter writer)
    {
        XmlSerializer keySerializer = new XmlSerializer(typeof(TKey));
        XmlSerializer valueSerializer = new XmlSerializer(typeof(TValue));

        foreach (TKey key in this.Keys)
        {
            writer.WriteStartElement("item");

            writer.WriteStartElement("key");
            keySerializer.Serialize(writer, key);
            writer.WriteEndElement();

            writer.WriteStartElement("value");
            TValue value = this[key];
            valueSerializer.Serialize(writer, value);
            writer.WriteEndElement();

            writer.WriteEndElement();
        }
    }
}

Есть еще какие-нибудь проблемы с XML-сериализацией?

19 ответов

Еще один важный момент: при выводе XML через веб-страницу (ASP.NET) не нужно включать метку порядка байтов в Юникоде. Конечно, способы использовать или не использовать спецификацию практически одинаковы:

ПЛОХО (включает в себя спецификацию):

XmlTextWriter wr = new XmlTextWriter(stream, new System.Text.Encoding.UTF8);

ХОРОШО:

XmlTextWriter  wr = new XmlTextWriter(stream, new System.Text.UTF8Encoding(false))

Вы можете явно передать false, чтобы указать, что вы не хотите спецификацию. Обратите внимание на четкую, очевидную разницу между Encoding.UTF8 а также UTF8Encoding,

Три дополнительных байта спецификации в начале: (0xEFBBBF) или (239 187 191).

Ссылка: http://chrislaco.com/blog/troubleshooting-common-problems-with-the-xmlserializer/

Я пока не могу комментировать, поэтому я прокомментирую пост Dr8k и сделаю еще одно замечание. Закрытые переменные, которые выставляются как общедоступные свойства getter/setter и сериализуются / десериализуются как таковые через эти свойства. Мы делали это на моей старой работе все время.

Однако следует отметить, что если у вас есть какая-либо логика в этих свойствах, она запускается, поэтому иногда порядок сериализации действительно имеет значение. Члены неявно упорядочены по тому, как они упорядочены в коде, но нет никаких гарантий, особенно когда вы наследуете другой объект. Явное их упорядочение - боль в тылу.

Я был сожжен этим в прошлом.

При сериализации в строку XML из потока памяти обязательно используйте MemoryStream#ToArray() вместо MemoryStream#GetBuffer(), иначе вы получите ненужные символы, которые не будут десериализованы должным образом (из-за выделенного дополнительного буфера).

http://msdn.microsoft.com/en-us/library/system.io.memorystream.getbuffer(VS.80).aspx

Если сериализатор обнаруживает элемент / свойство, интерфейс которого имеет тип, он не сериализуется. Например, следующее не будет сериализовано в XML:

public class ValuePair
{
    public ICompareable Value1 { get; set; }
    public ICompareable Value2 { get; set; }
}

Хотя это будет сериализовать:

public class ValuePair
{
    public object Value1 { get; set; }
    public object Value2 { get; set; }
}

IEnumerables<T> которые генерируются через возврат доходности, не сериализуются. Это связано с тем, что компилятор генерирует отдельный класс для реализации return и этот класс не помечается как сериализуемый.

Вы не можете сериализовать свойства только для чтения. У вас должны быть методы получения и установки, даже если вы никогда не собираетесь использовать десериализацию для превращения XML в объект.

По той же причине вы не можете сериализовать свойства, которые возвращают интерфейсы: десериализатор не будет знать, какой конкретный класс создать.

О, вот хороший: поскольку код сериализации XML генерируется и помещается в отдельную DLL, вы не получите какой-либо значимой ошибки, когда в вашем коде есть ошибка, которая нарушает сериализатор. Просто что-то вроде "невозможно найти s3d3fsdf.dll". Ницца.

Невозможно сериализовать объект, который не имеет конструктора без параметров (только что его укусил).

И по какой-то причине из следующих свойств Value сериализуется, но не FullName:

    public string FullName { get; set; }
    public double Value { get; set; }

Я никогда не удосужился разобраться, почему, я просто изменил значение на внутреннее...

Еще одна вещь, на которую следует обратить внимание: вы не можете сериализовать закрытые / защищенные члены класса, если вы используете сериализацию XML по умолчанию.

Но вы можете указать собственную логику сериализации XML, реализующую IXmlSerializable в вашем классе, и сериализовать любые частные поля, которые вам нужны / нужны.

http://msdn.microsoft.com/en-us/library/system.xml.serialization.ixmlserializable.aspx

См. " Поддержка связывания атрибутов языка определения расширенной схемы XML" для получения подробной информации о том, что поддерживается сериализатором XML, и о том, как поддерживаются поддерживаемые функции XSD.

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

System.InvalidOperationException: There was an error generating the XML document.
---System.InvalidCastException: Unable to cast object
of type 'MyNamespace.Settings' to type 'MyNamespace.Settings'. at
Microsoft.Xml.Serialization.GeneratedAssembly.
  XmlSerializationWriterSettings.Write3_Settings(Object o)

Причиной этого для меня был плагин, загруженный с использованием контекста LoadFrom, который имеет много недостатков при использовании контекста Load. Довольно забавно отслеживать это.

Если вы попытаетесь сериализовать массив, List<T>, или же IEnumerable<T> который содержит экземпляры подклассов T, вам нужно использовать XmlArrayItemAttribute для получения списка всех используемых подтипов. В противном случае вы получите бесполезный System.InvalidOperationException во время выполнения при сериализации.

Вот часть полного примера из документации

public class Group
{  
   /* The XmlArrayItemAttribute allows the XmlSerializer to insert both the base 
      type (Employee) and derived type (Manager) into serialized arrays. */

   [XmlArrayItem(typeof(Manager)), XmlArrayItem(typeof(Employee))]
   public Employee[] Employees;

Вы можете столкнуться с проблемами при сериализации объектов типа Color и / или Font.

Вот советы, которые мне помогли:

http://www.codeproject.com/KB/XML/xmlsettings.aspx

http://www.codeproject.com/KB/cs/GenericXmlSerializition.aspx

Свойства, отмеченные Obsolete атрибут не сериализован. Я не проверял с Deprecated атрибут, но я предполагаю, что он будет действовать так же.

Закрытые переменные / свойства не сериализуются в механизме по умолчанию для сериализации XML, но находятся в двоичной сериализации.

Будьте осторожны при сериализации типов без явной сериализации, это может привести к задержкам при их создании в.Net. Я обнаружил это недавно во время сериализации RSAParameters.

Если ваш XSD использует группы замещения, скорее всего, вы не сможете (де) сериализовать его автоматически. Вам нужно написать свои собственные сериализаторы для обработки этого сценария.

Например.

<xs:complexType name="MessageType" abstract="true">
    <xs:attributeGroup ref="commonMessageAttributes"/>
</xs:complexType>

<xs:element name="Message" type="MessageType"/>

<xs:element name="Envelope">
    <xs:complexType mixed="false">
        <xs:complexContent mixed="false">
            <xs:element ref="Message" minOccurs="0" maxOccurs="unbounded"/>
        </xs:complexContent>
    </xs:complexType>
</xs:element>

<xs:element name="ExampleMessageA" substitutionGroup="Message">
    <xs:complexType mixed="false">
        <xs:complexContent mixed="false">
                <xs:attribute name="messageCode"/>
        </xs:complexContent>
    </xs:complexType>
</xs:element>

<xs:element name="ExampleMessageB" substitutionGroup="Message">
    <xs:complexType mixed="false">
        <xs:complexContent mixed="false">
                <xs:attribute name="messageCode"/>
        </xs:complexContent>
    </xs:complexType>
</xs:element>

В этом примере конверт может содержать сообщения. Однако сериализатор по умолчанию.NET не различает Message, ExampleMessageA и ExampleMessageB. Он будет сериализован только в базовый класс Message и из него.

Я не могу объяснить это, но я обнаружил, что это не будет сериализовано:

[XmlElement("item")]
public myClass[] item
{
    get { return this.privateList.ToArray(); }
}

но это будет:

[XmlElement("item")]
public List<myClass> item
{
    get { return this.privateList; }
}

А также стоит отметить, что если вы сериализуетесь в memstream, вы можете искать 0, прежде чем использовать его.

Частные переменные / свойства не сериализуются в XML-сериализации, но находятся в двоичной сериализации.

Я полагаю, что это также поможет вам, если вы выставляете закрытые элементы через открытые свойства - закрытые члены не сериализуются, поэтому все открытые члены ссылаются на нулевые значения.

Другие вопросы по тегам