Der Deserializer hat keine Kenntnis von jeglicher Art, die zu diesem Vertrag abbildet
-
09-09-2019 - |
Frage
Ich versuche, einen Baum von Knoten Objekte zu serialisieren und deserialisieren. Meine abstrakten „Node“ Klasse sowie anderen abstrakten und konkreten Klassen, die sich daraus ableiten, sind in meinem „Informa“ Projekt definiert. Darüber hinaus habe ich eine statische Klasse in Informa für die Serialisierung / Deserialisierung erstellt.
Zuerst ich dekonstruiert meinen Baum in eine flache Liste des Typs Wörterbuch (guid, Knoten) , wo guid ist die eindeutige ID von Knoten.
Ich bin in der Lage alle mit einem Problem meines Knoten serialisiert. Aber wenn ich versuche deserialisieren ich die folgenden Ausnahme erhalten.
Fehler in Zeile 1, Position 227. Element ‚ http://schemas.microsoft.com/2003/10/Serialization/Arrays : Wert ‘ enthält die Daten der 'Informa: Building' Datenvertrag. Das Deserializer hat keine Kenntnisse auf jedem Typ, der zu diesem Vertrag abbildet. Hinzufügen der Typ ‚Gebäude‘ entspricht, in die Liste der bekannten Typen - für beispielsweise durch das usying Knowntype oder durch das Hinzufügen die Liste der bekannten Arten übergeben Datacontract Serializer.
Alle Klassen, die von Knoten ableiten, einschließlich Gebäude, haben die [Knowntype (typeof (Typ t))] Attribut auf sie angewendet.
Meine Serialisierung und Deserialisierung Methoden sind unter:
public static void SerializeProject(Project project, string filePath)
{
try
{
Dictionary<Guid, Node> nodeDic = DeconstructProject(project);
Stream stream = new FileStream(filePath, FileMode.Create, FileAccess.Write, FileShare.None);
//serialize
DataContractSerializer ser = new DataContractSerializer(typeof(Dictionary<Guid, Node>),"InformaProject","Informa");
ser.WriteObject(stream,nodeDic);
// Cleanup
stream.Close();
}
catch (Exception e)
{
MessageBox.Show("There was a problem serializing " + Path.GetFileName(filePath) + ". \n\nException:" + e.Message, "Doh!", MessageBoxButtons.OK, MessageBoxIcon.Error);
throw e;
}
}
public static Project DeSerializeProject(string filePath)
{
try
{
Project proj;
// Read the file back into a stream
Stream stream = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.Read);
DataContractSerializer ser = new DataContractSerializer(typeof(Dictionary<Guid, Node>), "InformaProject", "Informa");
Dictionary<Guid, Node> nodeDic = (Dictionary<Guid, Node>)ser.ReadObject(stream);
proj = ReconstructProject(nodeDic);
// Cleanup
stream.Close();
return proj;
}
catch (Exception e)
{
MessageBox.Show("There was a problem deserializing " + Path.GetFileName(filePath) + ". \n\nException:" + e.Message, "Doh!", MessageBoxButtons.OK, MessageBoxIcon.Error);
return null;
}
}
Lösung
Alle Klassen, die von Knoten ableiten, einschließlich Gebäude, haben die [Knowntype (typeof (Typ T))] -Attribut angewandt zu ihnen.
KnownType
ist in der Regel auf die Basis angelegt Typ - d. H
[DataContract, KnownType(typeof(Building)), ...]
abstract class Node { ... }
(beachten Sie - Sie auch die bekannten Typen im DataContractSerializer
Konstruktor angeben können, ohne Attribute erforderlich)
EDIT RE Ihre Antwort
Wenn die framwork Klasse nicht weiß, über alle abgeleiteten Typen, dann müssen Sie die bekannten Typen angeben, wenn die Serializer erstellen:
[DataContract] abstract class SomeBase { }
[DataContract] class Foo : SomeBase { }
[DataContract] class Bar : SomeBase { }
...
// here the knownTypes argument is important
new DataContractSerializer(typeof(SomeBase),
new Type[] { typeof(Foo), typeof(Bar) });
Dies kann mit kombiniert werden (zum Beispiel) preserveObjectReferences
usw. durch die null
im vorherigen Beispiel zu ersetzen.
END EDIT
Doch ohne etwas reproduzierbar (das heißt Node
und Building
), wird es viel zu schwer sein, zu helfen.
Die andere seltsame Sache; Bäume Strukturen sind sehr gut , um Dinge wie DataContractSerializer
- es gibt in der Regel keine Notwendigkeit, sie zuerst zu glätten, da Bäume können trivialerweise in XML ausgedrückt werden. Haben Sie wirklich brauchen, es zu glätten?
Beispiel:
using System;
using System.Collections.Generic;
using System.IO;
using System.Runtime.Serialization;
using System.Xml;
[DataContract, KnownType(typeof(Building))]
abstract class Node {
[DataMember]
public int Foo {get;set;}
}
[DataContract]
class Building : Node {
[DataMember]
public string Bar {get;set;}
}
static class Program
{
static void Main()
{
Dictionary<Guid, Node> data = new Dictionary<Guid, Node>();
Type type = typeof(Dictionary<Guid, Node>);
data.Add(Guid.NewGuid(), new Building { Foo = 1, Bar = "a" });
StringWriter sw = new StringWriter();
using (XmlWriter xw = XmlWriter.Create(sw))
{
DataContractSerializer dcs = new DataContractSerializer(type);
dcs.WriteObject(xw, data);
}
string xml = sw.ToString();
StringReader sr = new StringReader(xml);
using (XmlReader xr = XmlReader.Create(sr))
{
DataContractSerializer dcs = new DataContractSerializer(type);
Dictionary<Guid, Node> clone = (Dictionary<Guid, Node>)
dcs.ReadObject(xr);
foreach (KeyValuePair<Guid, Node> pair in clone)
{
Console.WriteLine(pair.Key + ": " + pair.Value.Foo + "/" +
((Building)pair.Value).Bar);
}
}
}
}
Andere Tipps
Alles klar hier ist ein Diagramm, das die Dinge klar machen sollte. Ich entwickle ein Plugin für ein anderes Programm, das Beziehungen und Eigenschaften nicht bereits in das Programm erstellt. Diese Beziehungen / Eigenschaften sind in meiner Baumstruktur definiert. Aber ich versuche, diese Struktur abstrakt zu definieren, so dass ich Implementierungen des Plugins für verschiedene Programme erstellen können, sowie den Zugang der Informationen von mehreren Implementierungen in einem einzigen „viewer“ Programm.
Meine Serialize / Deserialize Methoden werden im Rahmen definiert, aber der Rahmen nicht weiß, über alle Implementierungen. Ich habe gehofft, ich vermeiden, könnte die Implementierungsprojekte zu den Speichern / Öffnen / serialize / deserialize Methoden im Rahmen Projekt eine Liste von Typen passieren zu müssen, aber es scheint, dass ich dies nicht vermeiden können? Ich denke, wenn der Sinn, die serialize / deserialize Methoden müssen Zugang zu den Typen haben sie Deserialisieren.
http://dl.getdropbox.com/u/113068/informa_framework.jpg <--- Größere Version alt text http://dl.getdropbox.com/u/113068/informa_framework.jpg
So ist dies nicht wirklich das Problem mit Gebäuden erklären, da es eine konkrete Klasse im Rahmen Projekt. Ich denke, was passiert ist, wenn ich die DataContractSerializer Zugang hat Serialisierung auf alle Objekte und deren KnowType Parameter und speichert sie richtig. Aber wenn ich deserialisieren ich meine DataContractSerializer mit Wörterbuch als Typ erstellen. So ist es kennt nur Knoten, aber nicht die abgeleiteten Klassen von Knoten.
new DataContractSerializer(typeof(Dictionary<Guid, Node>))
Ich kann es nicht sagen, was alle abgeleiteten Typen sind, weil, wie ich sagte, dass sie in anderen Projekten, die das Rahmenprojekt nicht kennt. Soooooooo scheint die beste Lösung, jede Implementierung haben, ist eine Liste von Knotentypen übergeben sie an die Serialisierung und Deserialisierung Methoden im Rahmen Projekt verwendet.
Ist das sinnvoll? Gibt es einen besseren Weg, dies zu tun?
Was ist der Unterschied zwischen diesen beiden Verwendungen der KnownTypes zuschreiben? Ich wusste nicht, dass Sie könnte / möchte festlegen, dass eine Klasse eine Knowntype eines anderen ist.
[DataContract]
[KnownType(typeof(Building))]
abstract class Node {
[DataMember]
public int Foo {get;set;}
}
[DataContract]
class Building : Node {
[DataMember]
public string Bar {get;set;}
}
[DataContract]
[KnownType(typeof(Node))]
abstract class Node {
[DataMember]
public int Foo {get;set;}
}
[KnownType(typeof(Building))]
class Building : Node {
[DataMember]
public string Bar {get;set;}
}