.Net XML серіалізація отриманих? [зачинено]


121

Я зіткнувся з декількома хетчами під час серіалізації 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-серіалізація є там?


Lookin для більш підводних каменів лол, ви могли б допомогти мені: stackoverflow.com/questions/2663836 / ...
шіммі Weitzhandler

1
Крім того , ви хочете поглянути на реалізацію Чарльза Feduke про serialzable словника, він зробив XML письменник повідомлення між відносяться членами постійних членами серіалізовать по серіалізатор за замовчуванням: deploymentzone.com/2008/09/19 / ...
Шиммі Вайцхандлер

Це не схоже на те, що воно цілком ловить усі гатчі. Я встановлюю IEqualityComparer в конструкторі, але це не серіалізується в цьому коді. Будь-які ідеї, як розширити цей словник, щоб включити цю частину інформації? Чи може ця інформація оброблятися через об'єкт Type?
ColinCren

Відповіді:


27

Ще одна величезна проблема: виводячи XML через веб-сторінку (ASP.NET), ви не хочете включати позначку байтового замовлення Unicode . Звичайно, способи використання або не використання BOM майже однакові:

BAD (включає BOM):

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

ДОБРО:

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

Ви можете явно передати помилкове значення, щоб вказати, що не хочете BOM. Зауважте чітку, очевидну різницю між Encoding.UTF8та UTF8Encoding.

Три додаткові байти BOM на початку - (0xEFBBBF) або (239 187 191).

Довідка: http://chrislaco.com/blog/troubleshooting-common-problems-with-the-xmlserializer/


4
Ваш коментар був би ще кориснішим, якби ви сказали нам не просто що, а чому.
Ніл

1
Це насправді не пов’язано з серіалізацією XML ... це лише проблема XmlTextWriter
Thomas Levesque

7
-1: Не пов'язано з питанням, і ви не повинні використовувати XmlTextWriterв .NET 2.0 або вище.
Джон Сондерс

Дуже корисне посилання. Дякую.
Аніл Вангарі

21

Я поки не можу коментувати, тому я прокоментую допис Dr8k та ще одне зауваження. Приватні змінні, які піддаються публічним властивостям getter / setter і отримують серіалізацію / десеріалізацію як такі через ці властивості. Ми це робили на моїй старій роботі ще в той час.

Однак слід зазначити, що якщо у цих властивостях є якась логіка, логіка запускається, тому іноді порядок серіалізації насправді має значення. Члени є неявно упорядковуються тим, як вони упорядковані в коді, але гарантій немає, особливо коли ви успадковуєте інший об'єкт. Явно їх замовлення - це біль у тилу.

Мене це спалювало в минулому.


17
Я знайшов цю публікацію під час пошуку способів чітко встановити порядок полів. Це робиться з атрибутами: [XmlElementAttribute (Order = 1)] public int Field {...} Нижня сторона: атрибут повинен бути вказаний для ВСІХ полів у класі та всіх його нащадків! IMO. Ви повинні додати це до своєї публікації.
Крістіан Діаконеску

15

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

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


3
прямо з документів "Зауважте, що буфер містить виділені байти, які можуть бути невикористані. Наприклад, якщо рядок" test "записаний в об'єкт MemoryStream, довжина буфера, повернутого з GetBuffer, становить 256, а не 4, з 252 байтами Щоб не використовувати тільки дані в буфері, використовуйте метод ToArray, проте ToArray створює копію даних у пам'яті. " msdn.microsoft.com/en-us/library/…
realgt

тільки що зараз це побачив. Більше не звучить як дурниця.
Джон Сондерс

Ніколи цього не чув, що корисно для налагодження.
Рікі

10

Якщо серіалізатор стикається з членом / властивістю, який має інтерфейс як свій тип, він не буде серіалізувати. Наприклад, наступне не буде серіалізуватись у 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; }
}

Якщо ви отримаєте виняток із повідомленням "Тип не вирішено для члена ...", це може бути, що відбувається.
Кайл Крулл

9

IEnumerables<T>які генеруються за рахунок прибутковості прибутковості, не піддаються серіалізації. Це пояснюється тим, що компілятор генерує окремий клас, щоб реалізувати віддачу, і цей клас не позначається як серіалізаційний.


Це стосується "іншої" серіалізації, тобто атрибуту [Serializable]. Це не працює і для XmlSerializer.
Тім Робінсон,


8

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

З тієї ж причини ви не можете серіалізувати властивості, що повертають інтерфейси: десеріалізатор не знатиме, який конкретний клас інстанціювати.


1
Насправді ви можете серіалізувати властивість колекції, навіть якщо у неї немає сеттера, але вона має бути ініціалізована в конструкторі, щоб дезаріалізація могла додати до неї елементи
Thomas Levesque,

7

О, це добре: оскільки код серіалізації XML генерується та розміщується в окремій DLL, ви не отримуєте жодної суттєвої помилки, коли в коді є помилка, яка порушує серіалізатор. Щось на зразок "не вдається знайти s3d3fsdf.dll". Приємно.


11
Ви можете генерувати цю DLL заздалегідь, використовуючи XML "Інструмент генератора серіалізаторів (Sgen.exe)" та розгорнувшись із вашим додатком.
huseyint

6

Неможливо серіалізувати об'єкт, який не має параметричного конструктора (просто його покусав).

І чомусь із наведених нижче властивостей значення отримує серіалізацію, але не FullName:

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

Я ніколи не стикався з розробкою чому, я просто змінив значення на внутрішнє ...


4
Конструктор без параметрів може бути приватним / захищеним. Цього буде достатньо для серіалізатора XML. Проблема з FullName насправді дивна, не повинно статися ...
Макс Галкін

@Yacoder: Може, тому, що не double?просто double?
абатищев

FullName був, ймовірно, nullі тому не буде генерувати жодної XML при серіалізації
Jesper

5

Ще одне, що слід зазначити: ви не можете серіалізувати приватних / захищених членів класу, якщо ви використовуєте серіалізацію XML "за замовчуванням".

Але ви можете вказати власну логіку серіалізації XML, реалізуючи IXmlSerializable у своєму класі, і серіалізувати будь-які приватні поля, які вам потрібні / потрібні.

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


4

Якщо збірка, створена 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. Досить весело відстежуючи це вниз.




4

Якщо ви спробуєте серіалізовать масив, 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;

3

Приватні змінні / властивості не серіалізуються в механізмі за замовчуванням для серіалізації XML, але є у двійковій серіалізації.


2
Так, якщо ви використовуєте серіалізацію XML за замовчуванням. Ви можете вказати власну логіку серіалізації XML, реалізуючи IXmlSerializable у своєму класі, і серіалізувати будь-які приватні поля, які вам потрібні / потрібні.
Макс Галкін

1
Ну, це правда. Я це відредагую. Але реалізація цього інтерфейсу - це біль у дупі від того, що я пам’ятаю.
Чарльз Грехем

3

Властивості, позначені Obsoleteатрибутом, не серіалізуються. Я не перевіряв Deprecatedатрибут, але я припускаю, що він би діяв так само.


2

Я справді не можу пояснити це, але я виявив, що це не буде серіалізувати:

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

але це:

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

А також варто зауважити, що якщо ви серіалізуєте до пам’яті, ви, можливо, захочете скористатися 0, перш ніж використовувати його.


Я думаю, що це тому, що він не може його відновити. У другому прикладі він може викликати item.Add (), щоб додати елементи до списку. Це не може зробити це спочатку.
ilitirit

18
Використання: [XmlArray ("item"), XmlArrayItem ("myClass", typeof (myClass))]
RvdK

1
ура за це! щодня дізнаватися щось
annakata


2

Якщо ваш 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 за замовчуванням не розрізняє повідомлення, ExampleMessageA та ExampleMessageB. Він буде серіалізуватися лише до та із базового класу повідомлень.


0

Приватні змінні / властивості не серіалізуються в серіалізації XML, а є у двійковій серіалізації.

Я вважаю, що це також отримує вас, якщо ви піддаєте приватних членів через публічну власність - приватні члени не серіалізуються, тому загальнодоступні члени посилаються на нульові значення.


Це не правда. Організатор публічної власності буде називатися і, імовірно, встановлюватиме приватного члена.
Джон Сондерс
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.