2010-01-13 8 views
11

Nel codice di esempio riportato di seguito, ottengo questo errore :Come posso serializzare un oggetto con una proprietà Dictionary <string, object>?

Elemento TestSerializeDictionary123.Customer.CustomProperties vom Typ System.Collections.Generic.Dictionary`2 [[System.String, mscorlib, versione = 2.0.0.0, Culture = neutral, PublicKeyToken = b77a5c561934e089], [System.Object, mscorlib, versione = 2.0.0.0, Culture = neutral, PublicKeyToken = b77a5c561934e089]] può non essere serializzato perché 012.351.641,061 milaimplementa IDictionary.

Quando estraggo la proprietà Dictionary, funziona fine.

Come posso serializzare questo oggetto Cliente con la proprietà del dizionario? O quale tipo di sostituzione per Dizionario posso usare che sarebbe serializzabile?

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 + ")"; 
     } 

    } 
} 

risposta

14

Nella nostra applicazione abbiamo finito per usare:

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

invece di:

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

che ha risolto il problema come DataContractSerializer supporta dizionario.

Un'altra soluzione è THS XML Serializable Generic Dictionary soluzione funziona anche nell'esempio di cui sopra, e non v'è una lunga discussione in quel collegamento da chi lo utilizza, potrebbe essere utile per le persone che lavorano con questo problema.

+0

Sebbene DataContractSerializer sia un buon suggerimento, non copre tutti gli scenari e sicuramente non offre lo stesso livello di granularità e definizione come fa XmlSerializer. –

0

ho appena trovato questo blog post by Rakesh Rajan che descrive una possibile soluzione:

Override XmlSerialization facendo il tipo di implementare la classe System.Xml.Serialization.IXmlSerializable. Definire come si desidera serializzare l'oggetto in XML nel metodo WriteXml e definire come è possibile ricreare l'oggetto da una stringa xml nel metodo ReadXml.

Ma questo non funzionerebbe in quanto il tuo dizionario contiene un object piuttosto che un tipo specifico.

4

Non puoi (a meno di fare tutto da solo, che è orribile); il serializzatore xml non ha idea di cosa fare con object, poiché non include i metadati di tipo nel formato wire. Un'opzione (hacky) sarebbe quella di eseguire lo streaming di tutti come stringhe ai fini della serializzazione, ma in questo caso è necessario scrivere molto codice di parsing (etc).

+0

Ho intenzione di downvotare, per suggerire che non può essere fatto. Deriva dall'interpretazione di questa domanda come se si volesse solo la serializzazione XML, ma comunque, credo di doverlo fare, solo per fare la mia parte per prevenire possibili disinformazioni :) Speriamo che l'OP possa chiarire se vuole solo la serializzazione XML, o il tuo post può essere adeguato di conseguenza. –

+0

Il tag xml-serialization si trova sulla domanda, suggerendo che l'OP è interessato alla serializzazione XML. –

0

Cosa fare per contrassegnare la classe Cliente come DataContract e le sue proprietà come DataMembers. Il serializzatore DataContract eseguirà la serializzazione per te.

1

È possibile utilizzare invece Binary serialization. (Assicurati solo che tutte le tue classi siano contrassegnate come [Serializable].Certo, non sarà in formato XML, ma non ha lista che come requisito :)

9

Ecco una classe dizionario generica che sa come serializzare stessa:

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

Questo non risolve il problema di avere un dizionario in cui i valori sono 'object'. – jsirr13

0

Prova Serializating attraverso 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) 
     { 
      ; 
     } 
    } 
Problemi correlati