it-swarm-ru.tech

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-сериализацией?

120
Kalid

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

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

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/troublesho-common-problems-with-the-xmlserializer/

27
Kalid

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

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

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

21
Charles Graham

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

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

15
realgt

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

10
bwillard

Если сериализатор обнаруживает элемент/свойство, интерфейс которого имеет тип, он не сериализуется. Например, следующее не будет сериализовано в 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; }
}
10
Allon Guralnek

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

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

8
Tim Robinson

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

7
Eric Z Beard

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

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

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

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

6
Benjol

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

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

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

5
Max Galkin

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

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

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

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

4
Max Galkin

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

4
John Saunders

Если вы попытаетесь сериализовать массив 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;
4
MarkJ

Если ваша сборка, созданная при сериализации 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 , который имеет много недостатков при использовании контекста загрузки. Довольно забавно отслеживать это.

4
user7116

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

3
James Hulse

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

3
Charles Graham

Если ваш 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 и из него.

2
ilitirit

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

2
Keith

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

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

но это будет:

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

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

2
annakata

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

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

0
Dr8k