2010-01-13 4 views
11

Im Beispielcode unten, bekomme ich diesen Fehler :Wie kann ich ein Objekt mit einer Eigenschaft Dictionary <string, object> serialisieren?

Element TestSerializeDictionary123.Customer.CustomProperties vom Typ System.Collections.Generic.Dictionary`2 [[System.String, Mscorlib Version = 2.0.0.0, Culture = neutral, PublicKeyToken = b77a5c561934e089], [System.Object, mscorlib, Version = 2.0.0.0, Culture = neutral, PublicKeyToken = b77a5c561934e089]] kann nicht, weil es serialisiert werden implementiert IDictionary.

Wenn ich das Wörterbuch Eigenschaft herausnehmen, es funktioniert feinen.

Wie kann ich dieses Customer-Objekt mit der Dictionary-Eigenschaft serialisieren? Oder welchen Ersatztyp für Dictionary kann ich verwenden, der serialisierbar wäre?

using System; 
using System.Collections.Generic; 
using System.Xml.Serialization; 
using System.IO; 
using System.Xml; 
using System.Text; 

namespace TestSerializeDictionary123 
{ 
    public class Program 
    { 
     static void Main(string[] args) 
     { 
      List<Customer> customers = Customer.GetCustomers(); 

      Console.WriteLine("--- Serializing ------------------"); 

      foreach (var customer in customers) 
      { 
       Console.WriteLine("Serializing " + customer.GetFullName() + "..."); 
       string xml = XmlHelpers.SerializeObject<Customer>(customer); 
       Console.WriteLine(xml); 
       Console.WriteLine("Deserializing ..."); 
       Customer customer2 = XmlHelpers.DeserializeObject<Customer>(xml); 
       Console.WriteLine(customer2.GetFullName()); 
       Console.WriteLine("---"); 
      } 

      Console.ReadLine(); 
     } 
    } 

    public static class StringHelpers 
    { 
     public static String UTF8ByteArrayToString(Byte[] characters) 
     { 
      UTF8Encoding encoding = new UTF8Encoding(); 
      String constructedString = encoding.GetString(characters); 
      return (constructedString); 
     } 

     public static Byte[] StringToUTF8ByteArray(String pXmlString) 
     { 
      UTF8Encoding encoding = new UTF8Encoding(); 
      Byte[] byteArray = encoding.GetBytes(pXmlString); 
      return byteArray; 
     } 
    } 

    public static class XmlHelpers 
    { 
     public static string SerializeObject<T>(object o) 
     { 
      MemoryStream ms = new MemoryStream(); 
      XmlSerializer xs = new XmlSerializer(typeof(T)); 
      XmlTextWriter xtw = new XmlTextWriter(ms, Encoding.UTF8); 
      xs.Serialize(xtw, o); 
      ms = (MemoryStream)xtw.BaseStream; 
      return StringHelpers.UTF8ByteArrayToString(ms.ToArray()); 
     } 

     public static T DeserializeObject<T>(string xml) 
     { 
      XmlSerializer xs = new XmlSerializer(typeof(T)); 
      MemoryStream ms = new MemoryStream(StringHelpers.StringToUTF8ByteArray(xml)); 
      XmlTextWriter xtw = new XmlTextWriter(ms, Encoding.UTF8); 
      return (T)xs.Deserialize(ms); 
     } 
    } 

    public class Customer 
    { 
     public int Id { get; set; } 
     public string FirstName { get; set; } 
     public string LastName { get; set; } 
     public string Street { get; set; } 
     public string Location { get; set; } 
     public string ZipCode { get; set; } 
     public Dictionary<string,object> CustomProperties { get; set; } 

     private int internalValue = 23; 

     public static List<Customer> GetCustomers() 
     { 
      List<Customer> customers = new List<Customer>(); 
      customers.Add(new Customer { Id = 1, FirstName = "Jim", LastName = "Jones", ZipCode = "23434" }); 
      customers.Add(new Customer { Id = 2, FirstName = "Joe", LastName = "Adams", ZipCode = "12312" }); 
      customers.Add(new Customer { Id = 3, FirstName = "Jack", LastName = "Johnson", ZipCode = "23111" }); 
      customers.Add(new Customer { Id = 4, FirstName = "Angie", LastName = "Reckar", ZipCode = "54343" }); 
      customers.Add(new Customer { Id = 5, FirstName = "Henry", LastName = "Anderson", ZipCode = "16623" }); 
      return customers; 
     } 

     public string GetFullName() 
     { 
      return FirstName + " " + LastName + "(" + internalValue + ")"; 
     } 

    } 
} 

Antwort

14

In unserer Anwendung landeten wir mit:

DataContractSerializer xs = new DataContractSerializer(typeof (T)); 

statt:

XmlSerializer xs = new XmlSerializer(typeof (T)); 

, die das Problem als DataContractSerializer Wörterbuch unterstützt gelöst.

Eine andere Lösung ist ths XML Serializable Generic Dictionary Workaround funktioniert auch in dem obigen Beispiel, und es gibt eine lange Diskussion über diesen Link von Menschen, die es verwenden, könnte für die Menschen mit diesem Problem nützlich sein.

+0

Obwohl der DataContractSerializer ein guter Vorschlag ist, deckt er nicht alle Szenarien ab und bietet definitiv nicht die gleiche Granularität und Definition wie der XmlSerializer. –

0

Ich habe gerade diese blog post by Rakesh Rajan, die eine mögliche Lösung beschreibt:

Aufschalten XmlSerialization durch die Art der System.Xml.Serialization.IXmlSerializable Klasse implementieren zu machen. Definieren Sie, wie das Objekt in der WriteXml-Methode in XML serialisiert werden soll, und definieren Sie, wie Sie das Objekt aus einer XML-Zeichenfolge in der ReadXml-Methode neu erstellen können.

Aber das würde nicht funktionieren, da Ihr Wörterbuch einen object statt eines bestimmten Typs enthält.

4

Sie können nicht (kurz, alles selbst zu tun, was schrecklich ist); Der XML-Serializer wird keine Ahnung haben, was mit object zu tun ist, da es im Drahtformat keine Typ-Metadaten enthält. Eine (hacky) -Option wäre, diese alle als Strings für die Zwecke der Serialisierung zu streamen, aber dann haben Sie eine Menge zusätzlichen Parsing (etc) -Code zum Schreiben.

+0

Ich werde Sie downvote, für den Vorschlag, dass es nicht getan werden kann. Es kommt von der Interpretation dieser Frage als XML-Serialisierung nur wollen, aber dennoch, ich fühle, ich sollte nur meinen Teil dazu beitragen, mögliche Fehlinformationen zu verhindern :) Hoffentlich kann das OP klären, ob er nur XML-Serialisierung will, oder Ihr Beitrag sein kann entsprechend angepasst. –

+0

Das XML-Serialisierungs-Tag steht in der Frage, was darauf hindeutet, dass das OP an der XML-Serialisierung interessiert ist. –

0

Wie wäre es, Kundenklasse als DataContract und seine Eigenschaften als DataMembers zu markieren. Der DataContract Serializer übernimmt die Serialisierung für Sie.

1

Sie können stattdessen Binary serialization verwenden. (Stellen Sie sicher, dass alle Ihre Klassen als [Serializable] markiert sind.Natürlich wird es nicht im XML-Format, aber Sie nicht, dass als Voraussetzung Liste :)

9

Hier ist eine generische Wörterbuch-Klasse, die weiß, wie man sich serialisiert:

public class XmlDictionary<T, V> : Dictionary<T, V>, IXmlSerializable { 
    [XmlType("Entry")] 
    public struct Entry { 
     public Entry(T key, V value) : this() { Key = key; Value = value; } 
     [XmlElement("Key")] 
     public T Key { get; set; } 
     [XmlElement("Value")] 
     public V Value { get; set; } 
    } 
    System.Xml.Schema.XmlSchema IXmlSerializable.GetSchema() { 
     return null; 
    } 
    void IXmlSerializable.ReadXml(System.Xml.XmlReader reader) { 
     this.Clear(); 
     var serializer = new XmlSerializer(typeof(List<Entry>)); 
     reader.Read(); // Why is this necessary? 
     var list = (List<Entry>)serializer.Deserialize(reader); 
     foreach (var entry in list) this.Add(entry.Key, entry.Value); 
     reader.ReadEndElement(); 
    } 
    void IXmlSerializable.WriteXml(System.Xml.XmlWriter writer) { 
     var list = new List<Entry>(this.Count); 
     foreach (var entry in this) list.Add(new Entry(entry.Key, entry.Value)); 
     XmlSerializer serializer = new XmlSerializer(list.GetType()); 
     serializer.Serialize(writer, list); 
    } 
    } 
+0

Dies löst nicht das Problem, ein Wörterbuch zu haben, in dem die Werte "Objekt" sind. – jsirr13

0

Versuchen Serializating durch BinaryFormatter

private void Deserialize() 
    { 
     try 
     { 
      var f_fileStream = File.OpenRead(@"dictionarySerialized.xml"); 
      var f_binaryFormatter = new System.Runtime.Serialization.Formatters.Binary.BinaryFormatter(); 
      myDictionary = (Dictionary<string, myClass>)f_binaryFormatter.Deserialize(f_fileStream); 
      f_fileStream.Close(); 
     } 
     catch (Exception ex) 
     { 
      ; 
     } 
    } 
    private void Serialize() 
    { 
     try 
     { 
      var f_fileStream = new FileStream(@"dictionarySerialized.xml", FileMode.Create, FileAccess.Write); 
      var f_binaryFormatter = new System.Runtime.Serialization.Formatters.Binary.BinaryFormatter(); 
      f_binaryFormatter.Serialize(f_fileStream, myDictionary); 
      f_fileStream.Close(); 
     } 
     catch (Exception ex) 
     { 
      ; 
     } 
    }