2013-11-27 17 views
8

Ich versuche, eine Klasse in eine XML-Datei zu speichern/zu laden, die generische Typen enthält, die eine DataContractSerializer verwenden. Ich habe das Speichern funktioniert, aber habe festgestellt, dass ich es nicht laden kann, weil ich nicht die Liste der bekannten Typen für den Deserializer habe.Kann ich Generics ohne einen Verweis auf den Typ deserialisieren?

Gibt es eine Möglichkeit zum Serialisieren/Deserialisieren dieser Klasse, die es mir erlauben würde, es zu deserialisieren, ohne direkt auf einen der gespeicherten Typen zu verweisen?

Hier ist meine SessionVariables Klasse, die ich zu Speichern/Laden versuche:

[DataContract] 
public class SessionVariables 
{ 
    [DataMember] 
    private Dictionary<Type, ISessionVariables> _sessionVariables = new Dictionary<Type, ISessionVariables>(); 
    private object _syncLock = new object(); 

    public T Get<T>() 
     where T : ISessionVariables, new() 
    { 
     lock (_syncLock) 
     { 
      ISessionVariables vars = null; 

      if (_sessionVariables.TryGetValue(typeof(T), out vars)) 
       return (T)vars; 

      vars = new T(); 
      _sessionVariables.Add(typeof(T), vars); 

      return (T)vars; 
     } 
    } 

    public IList<Type> GetKnownTypes() 
    { 
     IList<Type> knownTypes = new List<Type>(); 

     knownTypes.Add(this.GetType().GetType()); // adds System.RuntimeType 

     foreach (Type t in _sessionVariables.Keys) 
     { 
      if (!knownTypes.Contains(t)) 
       knownTypes.Add(t); 
     } 

     return knownTypes; 
    } 
} 

Die verschiedenen Module der Anwendung erweitern die ISessionVariables Schnittstelle ihre eigene Reihe von Session-Variablen zu erstellen, wie folgt aus:

[DataContract] 
public class ModuleASessionVariables : ISessionVariables 
{ 
    [DataMember] 
    public string ModuleA_Property1{ get; set; } 
    [DataMember] 
    public string ModuleA_Property2 { get; set; } 
} 


[DataContract] 
public class ModuleBSessionVariables : ISessionVariables 
{ 
    [DataMember] 
    public string ModuleB_Property1{ get; set; } 
    [DataMember] 
    public string ModuleB_Property2 { get; set; } 
} 

Und eine Singleton-Instanz der SessionVariables Klasse wird verwendet, Session-Variablen zuzugreifen, wie folgt aus:

habe ich das speichern wie diese Arbeit:

using (FileStream writer = new FileStream(@"C:\test.txt", FileMode.Create)) 
{ 
    DataContractSerializer dcs = new DataContractSerializer(typeof(SessionVariables), singletonSessionVariables.GetKnownTypes()); 
    dcs.WriteObject(writer, singletonSessionVariables); 
    writer.Close(); 
} 

Doch diese Methode funktioniert nicht, um die Klasse deserialisieren, weil ich nicht weiß, es Arten bekannt ist.

Kann ich generische Typen serialisieren und deserialisieren, wenn ich keine direkten Bibliotheksreferenzen für einen der verwendeten Typen habe? Und wenn ja, wie?

+1

:/Ich würde den NetDataContractSerializer verwenden, der Typinformationen im Ergebnis enthält. – Will

+0

Denken Sie nicht, dass es möglich ist: Wie werden wir eine Instanz eines Typs erstellen, wenn der Typ selbst unbekannt ist? – Tigran

+0

@Will Danke, mir war nicht bewusst, dass es vorher schon existierte! Du solltest eine Antwort dafür schreiben :) – Rachel

Antwort

3

Das hier Problem ist, dass Sie nicht nur Daten serialisiert werden wollen, aber Sie wollen auch Daten über Ihre Daten, das heißt (CUE die dramatische Chipmunk) Metadaten serialisiert werden.

Diese Metadaten sind in diesem Fall die Typen der Modelle, die ursprünglich die Daten enthielten. Normalerweise ist dies kein Problem, aber wie Sie herausgefunden haben, wenn Sie Polymorphismus in Ihrem Entwurf nutzen, kann Ihre einzelne Sammlung zwei oder mehr verschiedene Typen enthalten, von denen jeder auf seinen ursprünglichen Typ deserialisiert werden muss.

Dies wird normalerweise erreicht, indem diese Typ-Metadaten im serialisierten Ergebnis gespeichert werden. Verschiedene Serialisierungsmethoden tun dies auf verschiedene Arten. Die Xaml-Serialisierung verwendet XML-Namespaces, die .NET-Namespaces zugeordnet sind, und benennt die Elemente nach dem ursprünglichen Typnamen. Json.net erreicht dies über einen bestimmten benannten Wert, der im json-Objekt gespeichert ist.

Der Standard-DataContractSerializer ist nicht typabhängig. Daher müssen Sie es durch eine Version ersetzen, die das .NET-Typsystem versteht und Typmetadaten serialisieren/deserialisieren kann. Zum Glück existiert im Framework bereits die NetDataContractSerializer.

Und das ist wie Sie eine Link-only-Antwort auffüllen. Die Aristokraten.

+0

Dies funktionierte großartig, ohne Bezug auf die externen Modulbibliotheken benötigt. Die einzige Änderung, die ich vornehmen musste, ist, das '_syncLock'-Objekt mit einem' [DataMember] '- Attribut zu markieren, oder es wird als 'null' deserialisiert. Bin jetzt auf der Suche nach einem Weg. Danke :) – Rachel

0

Sie könnten dies mit einem benutzerdefinierten DataContractResolver erreichen. Auf diese Weise können Sie sich in die Deserialisierungspipeline einklinken und einen Typ für die Deserialisierung basierend auf dem Typ/Namespace bereitstellen, der im serialisierten Diagramm gefunden wird.

Hier ist ein guter Artikel über sie: http://blogs.msdn.com/b/carlosfigueira/archive/2011/09/21/wcf-extensibility-data-contract-resolver.aspx

IDesign hat eine Implementierung eines Resolver, der für die dynamische Erkennung von Typen auf ihrer Website verwendet werden können: http://idesign.net/Downloads/GetDownload/1848 (Sie werden wahrscheinlich einige Änderungen vornehmen müssen, um Generika zu handhaben)

+1

Hinweis für Sie: Diese Lösung erfordert .net 4-- Ich bemerkte gerade das .net3-5-Tag in Ihrer Frage. – JMarsch

Verwandte Themen