Сериализация XML в .NET допускает полиморфные объекты через параметр extraTypes[]
конструктора XmlSerializer
. Это также позволяет настраивать сериализацию XML для типов, которые реализуют IXmlSerializable
.
Однако я не могу объединить эти две функции - как показано в этом минимальном примере:
using System;
using System.IO;
using System.Xml;
using System.Xml.Schema;
using System.Xml.Serialization;
namespace CsFoo
{
public class CustomSerializable : IXmlSerializable
{
public XmlSchema GetSchema() { return null; }
public void ReadXml(XmlReader xr) { }
public void WriteXml(XmlWriter xw) { }
}
class CsFoo
{
static void Main()
{
XmlSerializer xs = new XmlSerializer(
typeof(object),
new Type[] { typeof(CustomSerializable) });
xs.Serialize(new StringWriter(), new CustomSerializable());
}
}
Последняя строка выдает System.InvalidOperationException
с этим сообщением:
Тип CsFoo.CustomSerializable не может использоваться в этом контексте для
использовать CsFoo.CustomSerializable в качестве параметра, вернуть
тип или член класса или структуры, параметр, возвращаемый
тип или член должен быть объявлен как тип CsFoo.CustomSerializable
(это не может быть объектом). Объекты типа CsFoo.CustomSerializable
не может использоваться в нетипизированных коллекциях, таких как ArrayLists.
Проходя через динамически генерируемые сборки XML, мы в конечном итоге возвращаемся к стандартному библиотечному коду .NET, вызывая:
System.Xml.Serialization.XmlSerializationWriter.WriteTypedPrimitive(
String, String, Object, Boolean) : Void
В свою очередь, это приводит к:
protected Exception CreateUnknownTypeException(Type type)
{
if (typeof(IXmlSerializable).IsAssignableFrom(type))
{
return new InvalidOperationException(
Res.GetString("XmlInvalidSerializable",
new object[] { type.FullName }));
}
// Rest omitted...
Отражатель показывает, что ресурс XmlInvalidSerializable
соответствует строке выше - то есть WriteTypedPrimitive
не нравится IXmlSerializable
.
Если мы сгенерируем неполиморфный сериализатор, вот так:
XmlSerializer xs = new XmlSerializer(typeof(CustomSerializable));
.NET сгенерирует вызов:
System.Xml.Serialization.XmlSerializationWriter.WriteSerializable(
IXmlSerializable, String, String, Boolean) : Void
Правильно обрабатывает IXmlSerializable
. Кто-нибудь знает, почему .NET не использует эту функцию в полиморфном случае? Глядя на C #, который генерирует сериализатор XML, мне кажется, что это можно сделать довольно легко. Вот некоторый код, полученный от сериализатора XML, с непроверенным решением:
void Write1_Object(string n, string ns, global::System.Object o,
bool isNullable, bool needType)
{
if ((object)o == null)
{
if (isNullable) WriteNullTagLiteral(n, ns);
return;
}
if (!needType)
{
System.Type t = o.GetType();
if (t == typeof(global::System.Object))
{
}
>>> patch begin <<<
+ else if (typeof(IXmlSerializable).IsAssignableFrom(t))
+ {
+ WriteSerializable((System.Xml.Serialization.IXmlSerializable)
((global::CsFoo.CustomSerializable)o),
+ @"CustomSerializable", @"", true, true);
+ }
>>> patch end <<<
else
{
WriteTypedPrimitive(n, ns, o, true);
return;
}
}
WriteStartElement(n, ns, o, false, null);
WriteEndElement(o);
}
Это пропущено по техническим причинам или просто из-за ограничений функции? Неподдерживаемая функция или мой идиотизм? Мои навыки Google не помогают мне.
Здесь я нашел несколько связанных вопросов, причем " C # Xml-Сериализация производного класса с использованием IXmlSerializable " является наиболее актуальной. Это заставляет меня поверить, что это просто невозможно.
В этом случае моя текущая мысль - внедрить реализацию IXmlSerializable
по умолчанию в корневой базовый класс. Тогда все будет IXmlSerializable
, и .NET не будет жаловаться. Я могу использовать Reflection.Emit для извлечения тел ReadXml
и WriteXml
для каждого конкретного типа, генерируя XML, который выглядел бы так же, как если бы я использовал библиотеку один.
Некоторые люди, сталкиваясь с проблемой сериализации XML, думают: «Я знаю, я буду использовать Reflection.Emit для генерации кода». Теперь у них две проблемы.
P.S. Заметка; Я знаю об альтернативах сериализации .NET XML и знаю, что у нее есть ограничения. Я также знаю, что сохранить POCO намного проще, чем иметь дело с абстрактными типами данных. Но у меня есть куча устаревшего кода, и мне нужна поддержка существующих схем XML.
Так что, хотя я ценю ответы, которые показывают, как легко это сделать в SomeOtherXML
, YAML
, XAML
, ProtocolBuffers
, DataContract
, RandomJsonLibrary
, Thrift
или вашей библиотеке MorseCodeBasedSerializeToMp3
- эй, я мог бы кое-что выучить - то, на что я надеюсь, это обходной путь сериализатора XML, если не решение.