.NET Framework
XmlSerializer
Поиск…
замечания
Не используйте XmlSerializer
для анализа HTML
. Для этого доступны специальные библиотеки, такие как HTML Agility Pack
Сериализовать объект
public void SerializeFoo(string fileName, Foo foo)
{
var serializer = new XmlSerializer(typeof(Foo));
using (var stream = File.Open(fileName, FileMode.Create))
{
serializer.Serialize(stream, foo);
}
}
Дезамеризовать объект
public Foo DeserializeFoo(string fileName)
{
var serializer = new XmlSerializer(typeof(Foo));
using (var stream = File.OpenRead(fileName))
{
return (Foo)serializer.Deserialize(stream);
}
}
Поведение: имя элемента карты для свойства
<Foo>
<Dog/>
</Foo>
public class Foo
{
// Using XmlElement
[XmlElement(Name="Dog")]
public Animal Cat { get; set; }
}
Поведение: имя массива массива для свойства (XmlArray)
<Store>
<Articles>
<Product/>
<Product/>
</Articles>
</Store>
public class Store
{
[XmlArray("Articles")]
public List<Product> Products {get; set; }
}
Форматирование: пользовательский формат DateTime
public class Dog
{
private const string _birthStringFormat = "yyyy-MM-dd";
[XmlIgnore]
public DateTime Birth {get; set;}
[XmlElement(ElementName="Birth")]
public string BirthString
{
get { return Birth.ToString(_birthStringFormat); }
set { Birth = DateTime.ParseExact(value, _birthStringFormat, CultureInfo.InvariantCulture); }
}
}
Эффективное создание нескольких сериализаторов с производными типами, заданными динамически
Откуда мы пришли
Иногда мы не можем предоставить все необходимые метаданные, необходимые для инфраструктуры XmlSerializer в атрибуте. Предположим, что у нас есть базовый класс сериализованных объектов, а некоторые из производных классов неизвестны базовому классу. Мы не можем поместить атрибут для всех классов, которые не известны во время разработки базового типа. У нас может быть другая команда, разрабатывающая некоторые из производных классов.
Что мы можем сделать
Мы можем использовать new XmlSerializer(type, knownTypes)
, но это будет операция O (N ^ 2) для N сериализаторов, по крайней мере, для обнаружения всех типов, представленных в аргументах:
// Beware of the N^2 in terms of the number of types.
var allSerializers = allTypes.Select(t => new XmlSerializer(t, allTypes));
var serializerDictionary = Enumerable.Range(0, allTypes.Length)
.ToDictionary (i => allTypes[i], i => allSerializers[i])
В этом примере базовый тип не знает о его производных типах, что является нормальным в ООП.
Эффективное выполнение
К счастью, существует метод, который решает эту конкретную проблему - эффективно использовать известные типы для нескольких сериализаторов:
Метод System.Xml.Serialization.XmlSerializer.FromTypes (Тип [])
Метод FromTypes позволяет эффективно создавать массив объектов XmlSerializer для обработки массива объектов Type.
var allSerializers = XmlSerializer.FromTypes(allTypes);
var serializerDictionary = Enumerable.Range(0, allTypes.Length)
.ToDictionary(i => allTypes[i], i => allSerializers[i]);
Вот полный пример кода:
using System;
using System.Collections.Generic;
using System.Xml.Serialization;
using System.Linq;
using System.Linq;
public class Program
{
public class Container
{
public Base Base { get; set; }
}
public class Base
{
public int JustSomePropInBase { get; set; }
}
public class Derived : Base
{
public int JustSomePropInDerived { get; set; }
}
public void Main()
{
var sampleObject = new Container { Base = new Derived() };
var allTypes = new[] { typeof(Container), typeof(Base), typeof(Derived) };
Console.WriteLine("Trying to serialize without a derived class metadata:");
SetupSerializers(allTypes.Except(new[] { typeof(Derived) }).ToArray());
try
{
Serialize(sampleObject);
}
catch (InvalidOperationException e)
{
Console.WriteLine();
Console.WriteLine("This error was anticipated,");
Console.WriteLine("we have not supplied a derived class.");
Console.WriteLine(e);
}
Console.WriteLine("Now trying to serialize with all of the type information:");
SetupSerializers(allTypes);
Serialize(sampleObject);
Console.WriteLine();
Console.WriteLine("Slides down well this time!");
}
static void Serialize<T>(T o)
{
serializerDictionary[typeof(T)].Serialize(Console.Out, o);
}
private static Dictionary<Type, XmlSerializer> serializerDictionary;
static void SetupSerializers(Type[] allTypes)
{
var allSerializers = XmlSerializer.FromTypes(allTypes);
serializerDictionary = Enumerable.Range(0, allTypes.Length)
.ToDictionary(i => allTypes[i], i => allSerializers[i]);
}
}
Выход:
Trying to serialize without a derived class metadata:
<?xml version="1.0" encoding="utf-16"?>
<Container xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
This error was anticipated,
we have not supplied a derived class.
System.InvalidOperationException: There was an error generating the XML document. ---> System.InvalidOperationException: The type Program+Derived was not expected. Use the XmlInclude or SoapInclude attribute to specify types that are not known statically.
at Microsoft.Xml.Serialization.GeneratedAssembly.XmlSerializationWriter1.Write2_Base(String n, String ns, Base o, Boolean isNullable, Boolean needType)
at Microsoft.Xml.Serialization.GeneratedAssembly.XmlSerializationWriter1.Write3_Container(String n, String ns, Container o, Boolean isNullable, Boolean needType)
at Microsoft.Xml.Serialization.GeneratedAssembly.XmlSerializationWriter1.Write4_Container(Object o)
at System.Xml.Serialization.XmlSerializer.Serialize(XmlWriter xmlWriter, Object o, XmlSerializerNamespaces namespaces, String encodingStyle, String id)
--- End of inner exception stack trace ---
at System.Xml.Serialization.XmlSerializer.Serialize(XmlWriter xmlWriter, Object o, XmlSerializerNamespaces namespaces, String encodingStyle, String id)
at System.Xml.Serialization.XmlSerializer.Serialize(XmlWriter xmlWriter, Object o, XmlSerializerNamespaces namespaces, String encodingStyle)
at System.Xml.Serialization.XmlSerializer.Serialize(XmlWriter xmlWriter, Object o, XmlSerializerNamespaces namespaces)
at Program.Serialize[T](T o)
at Program.Main()
Now trying to serialize with all of the type information:
<?xml version="1.0" encoding="utf-16"?>
<Container xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<Base xsi:type="Derived">
<JustSomePropInBase>0</JustSomePropInBase>
<JustSomePropInDerived>0</JustSomePropInDerived>
</Base>
</Container>
Slides down well this time!
Что в выходе
Это сообщение об ошибке рекомендует, что мы пытались избежать (или то, что мы не можем сделать в некоторых сценариях) - ссылки на производные типы из базового класса:
Use the XmlInclude or SoapInclude attribute to specify types that are not known statically.
Вот как мы получаем наш производный класс в XML:
<Base xsi:type="Derived">
Base
соответствует типу свойства, объявленному в типе Container
, и Derived
является типом экземпляра, фактически поставленного.
Вот рабочий пример скрипки