.NET Framework
XmlSerializer
Szukaj…
Uwagi
Nie używaj XmlSerializer
do analizowania HTML
. W tym celu dostępne są specjalne biblioteki, takie jak HTML Agility Pack
Serializuj obiekt
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);
}
}
Deserializuj obiekt
public Foo DeserializeFoo(string fileName)
{
var serializer = new XmlSerializer(typeof(Foo));
using (var stream = File.OpenRead(fileName))
{
return (Foo)serializer.Deserialize(stream);
}
}
Zachowanie: odwzoruj nazwę elementu na właściwość
<Foo>
<Dog/>
</Foo>
public class Foo
{
// Using XmlElement
[XmlElement(Name="Dog")]
public Animal Cat { get; set; }
}
Zachowanie: Odwzoruj nazwę tablicy na właściwość (XmlArray)
<Store>
<Articles>
<Product/>
<Product/>
</Articles>
</Store>
public class Store
{
[XmlArray("Articles")]
public List<Product> Products {get; set; }
}
Formatowanie: niestandardowy format daty i godziny
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); }
}
}
Wydajne budowanie wielu serializatorów z typami pochodnymi określonymi dynamicznie
Skąd pochodzimy
Czasami nie możemy dostarczyć wszystkich wymaganych metadanych wymaganych dla struktury XmlSerializer w atrybucie. Załóżmy, że mamy klasę podstawową szeregowanych obiektów, a niektóre z klas pochodnych są nieznane klasie podstawowej. Nie możemy umieścić atrybutu dla wszystkich klas, które nie są znane w czasie projektowania typu podstawowego. Możemy mieć inny zespół opracowujący niektóre z pochodnych klas.
Co możemy zrobić
Możemy użyć new XmlSerializer(type, knownTypes)
, ale byłaby to operacja O (N ^ 2) dla N serializatorów, przynajmniej w celu wykrycia wszystkich typów podanych w argumentach:
// 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])
W tym przykładzie typ podstawowy nie jest świadomy swoich typów pochodnych, co jest normalne w OOP.
Robi to wydajnie
Na szczęście istnieje metoda, która rozwiązuje ten konkretny problem - efektywne dostarczanie znanych typów dla wielu serializatorów:
System.Xml.Serialization.XmlSerializer.FromTypes Metoda (typ [])
Metoda FromTypes umożliwia wydajne tworzenie tablicy obiektów XmlSerializer w celu przetworzenia tablicy obiektów Type.
var allSerializers = XmlSerializer.FromTypes(allTypes);
var serializerDictionary = Enumerable.Range(0, allTypes.Length)
.ToDictionary(i => allTypes[i], i => allSerializers[i]);
Oto pełna próbka kodu:
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]);
}
}
Wynik:
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!
Co jest w wyjściu
Ten komunikat o błędzie zaleca to, czego próbowaliśmy uniknąć (lub czego nie możemy zrobić w niektórych scenariuszach) - odwołując się do typów pochodnych z klasy podstawowej:
Use the XmlInclude or SoapInclude attribute to specify types that are not known statically.
Oto jak otrzymujemy naszą klasę pochodną w XML:
<Base xsi:type="Derived">
Base
odpowiada typowi właściwości zadeklarowanemu w typie Container
, a Derived
jest typem faktycznie dostarczonej instancji.
Oto działające przykładowe skrzypce