Buscar..


Observaciones

No utilice el XmlSerializer para analizar HTML . Para esto, bibliotecas especiales están disponibles como el HTML Agility Pack.

Serializar objeto

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);
    }
}

Deserializar objeto

public Foo DeserializeFoo(string fileName)
{
    var serializer = new XmlSerializer(typeof(Foo));
    using (var stream = File.OpenRead(fileName))
    {
        return (Foo)serializer.Deserialize(stream);
    }
}

Comportamiento: Asignar nombre de elemento a propiedad

<Foo>
    <Dog/>
</Foo>
public class Foo
{
    // Using XmlElement
    [XmlElement(Name="Dog")]
    public Animal Cat { get; set; }
}

Comportamiento: asignar el nombre de la matriz a la propiedad (XmlArray)

<Store>
    <Articles>
        <Product/>
        <Product/>
    </Articles>
</Store>
public class Store
{
    [XmlArray("Articles")]
    public List<Product> Products {get; set; }
}

Formato: Formato de fecha y hora personalizado

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); }
    }
}

Creación eficiente de varios serializadores con tipos derivados especificados dinámicamente

De donde venimos

A veces no podemos proporcionar todos los metadatos necesarios para el marco de XmlSerializer en atributo. Supongamos que tenemos una clase base de objetos serializados, y algunas de las clases derivadas son desconocidas para la clase base. No podemos colocar un atributo para todas las clases que no se conocen en el momento del diseño del tipo base. Podríamos tener otro equipo desarrollando algunas de las clases derivadas.

Qué podemos hacer

Podemos usar el new XmlSerializer(type, knownTypes) , pero eso sería una operación O (N ^ 2) para N serializadores, al menos para descubrir todos los tipos proporcionados en los argumentos:

// 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])

En este ejemplo, el tipo Base no tiene conocimiento de sus tipos derivados, lo cual es normal en OOP.

Haciéndolo eficientemente

Afortunadamente, hay un método que resuelve este problema en particular: suministrar tipos conocidos para varios serializadores de manera eficiente:

System.Xml.Serialization.XmlSerializer.FromTypes Method (Type [])

El método FromTypes le permite crear de manera eficiente una matriz de objetos XmlSerializer para procesar una matriz de objetos Tipo.

var allSerializers = XmlSerializer.FromTypes(allTypes);
var serializerDictionary = Enumerable.Range(0, allTypes.Length)
    .ToDictionary(i => allTypes[i], i => allSerializers[i]);

Aquí hay un ejemplo completo de código:

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]);
    }
}

Salida:

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!

¿Qué hay en la salida?

Este mensaje de error recomienda lo que intentamos evitar (o lo que no podemos hacer en algunos escenarios): hacer referencia a los tipos derivados de la clase base:

Use the XmlInclude or SoapInclude attribute to specify types that are not known statically.

Así es como obtenemos nuestra clase derivada en el XML:

<Base xsi:type="Derived">

Base corresponde al tipo de propiedad declarado en el tipo Container , y Derived es el tipo de la instancia realmente suministrada.

Aquí hay un ejemplo de trabajo.



Modified text is an extract of the original Stack Overflow Documentation
Licenciado bajo CC BY-SA 3.0
No afiliado a Stack Overflow