Kann ich eine generische Liste serialisierbarer Objekte serialisieren, ohne deren Typ angeben zu müssen?
So etwas wie die Absicht hinter dem kaputten Code unten:
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);
}
Bearbeiten:
Für diejenigen, die Details wissen wollten: Wenn ich versuche, diesen Code auszuführen, tritt in der XMLSerializer-Zeile [...] ein Fehler auf mit:
Schnittstelle System.Runtime.Serialization.ISerializable kann nicht serialisiert werden.
Wenn ich zu ändere, List<object>
bekomme ich "There was an error generating the XML document."
. Das InnerException-Detail ist"{"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."}"
Das Personenobjekt ist wie folgt definiert:
[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; }
}
}
Die PersonList ist nur eine List<Person>
.
Dies ist jedoch nur zum Testen gedacht, daher waren die Details nicht zu wichtig. Der Schlüssel ist, dass ich ein oder mehrere verschiedene Objekte habe, die alle serialisierbar sind. Ich möchte sie alle in eine Datei serialisieren. Ich dachte, der einfachste Weg, dies zu tun, wäre, sie in eine generische Liste aufzunehmen und die Liste auf einmal zu serialisieren. Das funktioniert aber nicht.
Ich habe es auch mit versucht List<IXmlSerializable>
, aber das scheitert mit
System.Xml.Serialization.IXmlSerializable cannot be serialized because it does not have a parameterless constructor.
Entschuldigen Sie den Mangel an Details, aber ich bin ein Anfänger und weiß nicht, welche Details erforderlich sind. Es wäre hilfreich, wenn Leute, die nach mehr Details fragen, versuchen würden, so zu antworten, dass ich verstehe, welche Details erforderlich sind, oder eine grundlegende Antwort mit möglichen Anweisungen.
Auch dank der beiden Antworten, die ich bisher erhalten habe, hätte ich viel mehr Zeit mit Lesen verbringen können, ohne diese Ideen zu bekommen. Es ist erstaunlich, wie hilfreich die Leute auf dieser Seite sind.
quelle
ISerializable
oder haben sie nur das[Serializable]
Attribut?Antworten:
Ich habe eine Lösung für eine generische Liste <> mit dynamisch gebundenen Elementen.
Klasse PersonalList ist das Stammelement
[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); } }
Klasse Person Es ist ein einzelnes Listenelement
[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; } }
Klasse SpecialPerson erbt Person
[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; } }
Klasse SuperPerson erbt Person
[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; } }
und die Haupttestquelle
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(); }
Wichtig ist die Definition und enthält die verschiedenen Typen.
quelle
Siehe Einführung in die XML-Serialisierung :
Insbesondere spielt
ISerializable
das[Serializable]
Attribut keine Rolle.Nachdem Sie uns nun mitgeteilt haben, was Ihr Problem ist ("es funktioniert nicht" ist keine Problemstellung), können Sie Antworten auf Ihr tatsächliches Problem erhalten, anstatt Vermutungen anzustellen.
Wenn Sie eine Sammlung eines Typs serialisieren, aber tatsächlich eine Sammlung von Instanzen abgeleiteter Typen serialisieren, müssen Sie dem Serializer mitteilen, welche Typen Sie tatsächlich serialisieren werden. Dies gilt auch für Sammlungen von
object
.Sie müssen den XmlSerializer-Konstruktor (Type, Type []) verwenden, um die Liste der möglichen Typen anzugeben .
quelle
Sie können eine Sammlung von Objekten nicht serialisieren, ohne die erwarteten Typen anzugeben. Sie müssen die Liste der erwarteten Typen an den Konstruktor von
XmlSerializer
(demextraTypes
Parameter) übergeben: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); }
Wenn alle Objekte Ihrer Liste von derselben Klasse erben, können Sie das
XmlInclude
Attribut auch verwenden , um die erwarteten Typen anzugeben:[XmlInclude(typeof(Foo)), XmlInclude(typeof(Bar))] public class MyBaseClass { }
quelle
xsi:type
auf das Element geschrieben, das das Objekt darstellt. Dies ermöglicht XmlSerializer, den tatsächlichen Typ des Objekts zu kennenIch denke, es ist am besten, wenn Sie Methoden mit generischen Argumenten verwenden, wie die folgenden:
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; }
quelle
XmlSerializer(Type)
Überlastung einer der sicher zu verwendenden Konstruktoren ist, da sie die zwischengespeicherten Assemblys wiederverwendet. (der andere ist öffentlichXmlSerializer(Type type, string defaultNamespace)
) support.microsoft.com/kb/886385/en-usIch denke, Dreas Ansatz ist in Ordnung. Eine Alternative dazu besteht jedoch darin, einige statische Hilfsmethoden zu haben und IXmlSerializable auf jeder Ihrer Methoden zu implementieren, z. B. eine XmlWriter-Erweiterungsmethode und die XmlReader-Methode, um sie zurückzulesen.
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; }
Wenn Sie die XmlSerializer-Klasse direkt verwenden, erstellen Sie nach Möglichkeit vorab Serialisierungsassemblys, da Sie beim regelmäßigen Erstellen neuer XmlSerializer einen großen Leistungseinbruch erzielen können.
Für eine Sammlung benötigen Sie ungefähr Folgendes:
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(); }
quelle
Unten ist eine Util-Klasse in meinem Projekt:
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); } } } }
quelle
IFormatter
ist als Antwort auf eine Frage zur XML-Serialisierung verwirrend. Außerdem sollten Sie werfenArgumentNullExeption
, nichtNullReferenceException
. Du solltest niemals werfenNullReferenceException
.Der einfachste Weg, den ich gefunden habe. Wenden Sie das
System.Xml.Serialization.XmlArray
Attribut darauf an.[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); }
Der Serializer erkennt, dass es sich um ein Array handelt, und serialisiert die Elemente der Liste als untergeordnete Knoten.
quelle
Mit dem Parameter knowTypeList können mit DataContractSerializer mehrere bekannte Typen serialisiert werden:
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); } } }
quelle
Wenn die XML-Ausgabeanforderung geändert werden kann, können Sie immer die binäre Serialisierung verwenden, die besser für die Arbeit mit heterogenen Objektlisten geeignet ist. Hier ist ein Beispiel:
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) //... } }
Verwendung als solche:
[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); }
quelle