it-swarm.dev

XML Serializovat obecný seznam serializovatelných objektů

Mohu serializovat obecný seznam serializovatelných objektů, aniž byste museli specifikovat jejich typ.

Něco jako záměr za níže uvedeným kódem:

List<ISerializable> serializableList = new List<ISerializable>();

XmlSerializer xmlSerializer = new XmlSerializer(serializableList.GetType());

serializableList.Add((ISerializable)PersonList);

using (StreamWriter streamWriter = System.IO.File.CreateText(fileName))
{
    xmlSerializer.Serialize(streamWriter, serializableList);
}

Upravit:

Pro ty, kteří se chtěli dozvědět podrobnosti: když se pokusím spustit tento kód, chyby na řádku XMLSerializer [...] s:

Nelze serializovat rozhraní System.Runtime.Serialization.ISerializable.

Pokud změním na List<object> dostanu "There was an error generating the XML document.". Detail InnerException je "{"The type System.Collections.Generic.List1[[Project1.Person, ConsoleFramework, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null]] may not be used in this context."}"

Objekt osoby je definován následovně:

[XmlRoot("Person")]
public class Person
{
    string _firstName = String.Empty;
    string _lastName = String.Empty;

    private Person()
    {
    }

    public Person(string lastName, string firstName)
    {
        _lastName = lastName;
        _firstName = firstName;
    }

    [XmlAttribute(DataType = "string", AttributeName = "LastName")]
    public string LastName
    {
        get { return _lastName; }
        set { _lastName = value; }
    }

    [XmlAttribute(DataType = "string", AttributeName = "FirstName")]
    public string FirstName
    {
        get { return _firstName; }
        set { _firstName = value; }
    }
}

PersonList je jen List<Person>.

Tohle je jen pro testování, takže necítil, že by detaily byly příliš důležité. Klíčem je, že mám jeden nebo více různých objektů, z nichž všechny jsou serializovatelné. Chci je všechny serializovat do jednoho souboru. Myslel jsem, že nejjednodušší způsob, jak to udělat, je dát je do obecného seznamu a serializovat seznam najednou. Ale to nefunguje.

Snažil jsem se také s List<IXmlSerializable>, ale to selže

System.Xml.Serialization.IXmlSerializable cannot be serialized because it does not have a parameterless constructor.

Omlouváme se za nedostatek detailů, ale já jsem začátečník v tomto a nevím, co je požadováno. Bylo by užitečné, kdyby se lidé, kteří žádají o podrobnější informace, snažili reagovat způsobem, který by mě nechal pochopit, jaké detaily jsou požadovány, nebo základní odpověď načrtávající možné směry.

Také díky na dvě odpovědi, které jsem doposud dostal - mohl jsem strávit mnohem více času čtením, aniž bych tyto myšlenky získal. Je úžasné, jak jsou na tomto webu užitečné lidé.

68
Simon D

Mám řešení pro obecný seznam <> s dynamickými vázanými položkami.

classList je to kořenový prvek

[XmlRoot("PersonenListe")]
[XmlInclude(typeof(Person))] // include type class Person
public class PersonalList
{
    [XmlArray("PersonenArray")]
    [XmlArrayItem("PersonObjekt")]
    public List<Person> Persons = new List<Person>();

    [XmlElement("Listname")]
    public string Listname { get; set; }

    // Konstruktoren 
    public PersonalList() { }

    public PersonalList(string name)
    {
        this.Listname = name;
    }

    public void AddPerson(Person person)
    {
        Persons.Add(person);
    }
}

třída Osoba je to jediný prvek seznamu 

[XmlType("Person")] // define Type
[XmlInclude(typeof(SpecialPerson)), XmlInclude(typeof(SuperPerson))]  
        // include type class SpecialPerson and class SuperPerson
public class Person
{
    [XmlAttribute("PersID", DataType = "string")]
    public string ID { get; set; }

    [XmlElement("Name")]
    public string Name { get; set; }

    [XmlElement("City")]
    public string City { get; set; }

    [XmlElement("Age")]
    public int Age { get; set; }

    // Konstruktoren 
    public Person() { }

    public Person(string name, string city, int age, string id)
    {
        this.Name = name;
        this.City = city;
        this.Age = age;
        this.ID = id;
    }
}

třída SpecialPerson dědí osobu

[XmlType("SpecialPerson")] // define Type
public class SpecialPerson : Person
{
    [XmlElement("SpecialInterests")]
    public string Interests { get; set; }

    public SpecialPerson() { }

    public SpecialPerson(string name, string city, int age, string id, string interests)
    {
        this.Name = name;
        this.City = city;
        this.Age = age;
        this.ID = id;
        this.Interests = interests;
    }
}

třídy SuperPerson dědí osobu

[XmlType("SuperPerson")] // define Type
public class SuperPerson : Person
{
    [XmlArray("Skills")]
    [XmlArrayItem("Skill")]
    public List<String> Skills { get; set; }

    [XmlElement("Alias")]
    public string Alias { get; set; }

    public SuperPerson() 
    {
        Skills = new List<String>();
    }

    public SuperPerson(string name, string city, int age, string id, string[] skills, string alias)
    {
        Skills = new List<String>();

        this.Name = name;
        this.City = city;
        this.Age = age;
        this.ID = id;
        foreach (string item in skills)
        {
            this.Skills.Add(item);   
        }
        this.Alias = alias;
    }
}

a hlavní testovací zdroj

static void Main(string[] args)
{
    PersonalList personen = new PersonalList(); 
    personen.Listname = "Friends";

    // normal person
    Person normPerson = new Person();
    normPerson.ID = "0";
    normPerson.Name = "Max Man";
    normPerson.City = "Capitol City";
    normPerson.Age = 33;

    // special person
    SpecialPerson specPerson = new SpecialPerson();
    specPerson.ID = "1";
    specPerson.Name = "Albert Einstein";
    specPerson.City = "Ulm";
    specPerson.Age = 36;
    specPerson.Interests = "Physics";

    // super person
    SuperPerson supPerson = new SuperPerson();
    supPerson.ID = "2";
    supPerson.Name = "Superman";
    supPerson.Alias = "Clark Kent";
    supPerson.City = "Metropolis";
    supPerson.Age = int.MaxValue;
    supPerson.Skills.Add("fly");
    supPerson.Skills.Add("strong");

    // Add Persons
    personen.AddPerson(normPerson);
    personen.AddPerson(specPerson);
    personen.AddPerson(supPerson);

    // Serialize 
    Type[] personTypes = { typeof(Person), typeof(SpecialPerson), typeof(SuperPerson) };
    XmlSerializer serializer = new XmlSerializer(typeof(PersonalList), personTypes); 
    FileStream fs = new FileStream("Personenliste.xml", FileMode.Create); 
    serializer.Serialize(fs, personen); 
    fs.Close(); 
    personen = null;

    // Deserialize 
    fs = new FileStream("Personenliste.xml", FileMode.Open); 
    personen = (PersonalList)serializer.Deserialize(fs); 
    serializer.Serialize(Console.Out, personen);
    Console.ReadLine();
}

Důležitá je definice a zahrnuje různé typy.

70
Damasch

Viz Představení XML serializace :

Položky, které mohou být serializovány

Následující položky lze serializovat pomocí třídy XmlSerializer :

  • Veřejné čtení/zápis vlastností a oblastí veřejných tříd
  • Třídy implementující ICollection nebo IEnumerable
  • XmlElement objekty
  • XmlNode objekty
  • DataSet objekty

Nezáleží zejména na ISerializable nebo atributu [Serializable].


Teď, když jste nám řekli, jaký je váš problém („nefunguje to“, není to prohlášení o problému), můžete namísto odhadů získat odpovědi na váš skutečný problém.

Když serializujete kolekci typu, ale ve skutečnosti bude serializovat kolekci instancí odvozených typů, musíte nechat serializátor vědět, které typy budou ve skutečnosti serializovány. To platí i pro sbírky object.

Je třeba použít XmlSerializer (Type, Type []) konstruktor, aby byl uveden seznam možných typů.

21
John Saunders

Nelze serializovat kolekci objektů bez určení očekávaných typů. Seznam očekávaných typů musíte předat konstruktoru XmlSerializer (parametr extraTypes):

List<object> list = new List<object>();
list.Add(new Foo());
list.Add(new Bar());

XmlSerializer xs = new XmlSerializer(typeof(object), new Type[] {typeof(Foo), typeof(Bar)});
using (StreamWriter streamWriter = System.IO.File.CreateText(fileName))
{
    xs.Serialize(streamWriter, list);
}

Pokud všechny objekty vašeho seznamu dědí ze stejné třídy, můžete také použít atribut XmlInclude k určení očekávaných typů:

[XmlInclude(typeof(Foo)), XmlInclude(typeof(Bar))]
public class MyBaseClass
{
}
5
Thomas Levesque

Myslím, že je nejlepší, když použijete metody s obecnými argumenty, jako je následující:

public static void SerializeToXml<T>(T obj, string fileName)
{
    using (var fileStream = new FileStream(fileName, FileMode.Create))
    { 
        var ser = new XmlSerializer(typeof(T)); 
        ser.Serialize(fileStream, obj);
    }
}

public static T DeserializeFromXml<T>(string xml)
{
    T result;
    var ser = new XmlSerializer(typeof(T));
    using (var tr = new StringReader(xml))
    {
        result = (T)ser.Deserialize(tr);
    }
    return result;
}
4
Andreas Grech

Myslím, že Dreasův přístup je v pořádku. Alternativou k tomu je však mít některé statické pomocné metody a implementovat IXmlSerializable na každé z vašich metod, např. Metodu rozšíření XmlWriter a XmlReader, aby si je mohl přečíst zpět.

public static void SaveXmlSerialiableElement<T>(this XmlWriter writer, String elementName, T element) where T : IXmlSerializable
{
   writer.WriteStartElement(elementName);
   writer.WriteAttributeString("TYPE", element.GetType().AssemblyQualifiedName);
   element.WriteXml(writer);
   writer.WriteEndElement();
}

public static T ReadXmlSerializableElement<T>(this XmlReader reader, String elementName) where T : IXmlSerializable
{
   reader.ReadToElement(elementName);

   Type elementType = Type.GetType(reader.GetAttribute("TYPE"));
   T element = (T)Activator.CreateInstance(elementType);
   element.ReadXml(reader);
   return element;
}

Pokud jdete dolů cestou použití třídy XmlSerializer přímo, vytvořte serializační sestavy před rukou, pokud je to možné, protože můžete provádět velký výkon při vytváření nových XmlSerializers pravidelně.

Pro kolekci potřebujete něco takového:

public static void SaveXmlSerialiazbleCollection<T>(this XmlWriter writer, String collectionName, String elementName, IEnumerable<T> items) where T : IXmlSerializable
{
   writer.WriteStartElement(collectionName);
   foreach (T item in items)
   {
      writer.WriteStartElement(elementName);
      writer.WriteAttributeString("TYPE", item.GetType().AssemblyQualifiedName);
      item.WriteXml(writer);
      writer.WriteEndElement();
   }
   writer.WriteEndElement();
}
3
Ian

Nejjednodušší způsob, jak to udělat, které jsem našel. Použijte atribut System.Xml.Serialization.XmlArray.

[System.Xml.Serialization.XmlArray] //This is the part that makes it work
List<object> serializableList = new List<object>();

XmlSerializer xmlSerializer = new XmlSerializer(serializableList.GetType());

serializableList.Add(PersonList);

using (StreamWriter streamWriter = System.IO.File.CreateText(fileName))
{
    xmlSerializer.Serialize(streamWriter, serializableList);
}

Serializer bude vyzvednout na to být pole a serializovat položky seznamu jako podřízené uzly.

2
Lee

Níže je uvedena třída Util v mém projektu:

namespace Utils
{
    public static class SerializeUtil
    {
        public static void SerializeToFormatter<F>(object obj, string path) where F : IFormatter, new()
        {
            if (obj == null)
            {
                throw new NullReferenceException("obj Cannot be Null.");
            }

            if (obj.GetType().IsSerializable == false)
            {
                //  throw new 
            }
            IFormatter f = new F();
            SerializeToFormatter(obj, path, f);
        }

        public static T DeserializeFromFormatter<T, F>(string path) where F : IFormatter, new()
        {
            T t;
            IFormatter f = new F();
            using (FileStream fs = File.OpenRead(path))
            {
                t = (T)f.Deserialize(fs);
            }
            return t;
        }

        public static void SerializeToXML<T>(string path, object obj)
        {
            XmlSerializer xs = new XmlSerializer(typeof(T));
            using (FileStream fs = File.Create(path))
            {
                xs.Serialize(fs, obj);
            }
        }

        public static T DeserializeFromXML<T>(string path)
        {
            XmlSerializer xs = new XmlSerializer(typeof(T));
            using (FileStream fs = File.OpenRead(path))
            {
                return (T)xs.Deserialize(fs);
            }
        }

        public static T DeserializeFromXml<T>(string xml)
        {
            T result;

            var ser = new XmlSerializer(typeof(T));
            using (var tr = new StringReader(xml))
            {
                result = (T)ser.Deserialize(tr);
            }
            return result;
        }


        private static void SerializeToFormatter(object obj, string path, IFormatter formatter)
        {
            using (FileStream fs = File.Create(path))
            {
                formatter.Serialize(fs, obj);
            }
        }
    }
}
2
ligaoren

Pokud lze změnit požadavek na výstup XML, můžete vždy použít binární serializaci - což je vhodnější pro práci s heterogenními seznamy objektů. Zde je příklad:

private void SerializeList(List<Object> Targets, string TargetPath)
{
    IFormatter Formatter = new BinaryFormatter();

    using (FileStream OutputStream = System.IO.File.Create(TargetPath))
    {
        try
        {
            Formatter.Serialize(OutputStream, Targets);
        } catch (SerializationException ex) {
            //(Likely Failed to Mark Type as Serializable)
            //...
        }
}

Použít jako takové: 

[Serializable]
public class Animal
{
    public string Home { get; set; }
}

[Serializable]
public class Person
{
    public string Name { get; set; }
}


public void ExampleUsage() {

    List<Object> SerializeMeBaby = new List<Object> {
        new Animal { Home = "London, UK" },
        new Person { Name = "Skittles" }
    };

    string TargetPath = Path.Combine(
        Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData),
        "Test1.dat");

    SerializeList(SerializeMeBaby, TargetPath);
}
0
Robert Venables

parametr knowTypeList nechte serializovat s DataContractSerializer několika známými typy:

private static void WriteObject(
        string fileName, IEnumerable<Vehichle> reflectedInstances, List<Type> knownTypeList)
    {
        using (FileStream writer = new FileStream(fileName, FileMode.Append))
        {
            foreach (var item in reflectedInstances)
            {
                var serializer = new DataContractSerializer(typeof(Vehichle), knownTypeList);
                serializer.WriteObject(writer, item);
            }
        }
    }
0