Как я могу сериализовать объект со свойством Dictionary <string, object>? - PullRequest
12 голосов
/ 13 января 2010

В приведенном ниже примере кода я получаю ошибка :

Элемент TestSerializeDictionary123.Customer.CustomProperties Вом Тип System.Collections.Generic.Dictionary`2 [[System.String, mscorlib, версия = 2.0.0.0, Culture = нейтрально, PublicKeyToken = b77a5c561934e089], [System.Object, mscorlib, версия = 2.0.0.0, Culture = нейтрально, PublicKeyToken = b77a5c561934e089]] может не быть сериализованным, потому что это реализует IDictionary.

Когда я беру свойство Dictionary, оно работает отлично .

Как я могу сериализовать этот объект Customer со свойством словаря? Или какой тип замены для словаря я могу использовать, который будет сериализуемым?

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

    }
}

Ответы [ 7 ]

14 голосов
/ 13 января 2010

В нашем приложении мы использовали:

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

вместо:

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

, которая решила проблему, так как DatacontractSerializer поддерживает словарь.

Другое решение - это XML Serializable Generic Dictionary Обходной путь также работает в приведенном выше примере, и по этой ссылке ведется длительное обсуждение со стороны людей, использующих его, может быть полезно для людей, работающих с этой проблемой.

9 голосов
/ 13 января 2010

Вот общий класс словаря, который знает, как сериализовать себя:

  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);
    }
  }
4 голосов
/ 13 января 2010

Вы не можете (если не делать все сами, что ужасно); Сериализатор xml не будет иметь понятия, что делать с object, так как он не включает метаданные типа в проводном формате. Один (хакерский) вариант - потоковая передача всех этих строк в целях сериализации, но тогда вам нужно написать много дополнительного кода (и т. Д.).

1 голос
/ 13 января 2010

Вместо этого вы можете использовать Двоичная сериализация . (Просто убедитесь, что все ваши классы помечены как [Serializable]. Конечно, это не будет в формате XML, но вы не указали это как требование:)

0 голосов
/ 20 февраля 2015

Попробуйте сериализацию через 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)
        {
            ;
        }
    }
0 голосов
/ 13 января 2010

Как насчет пометить класс Customer как DataContract, а его свойства - как DataMembers. Сериализатор DataContract выполнит сериализацию за вас.

0 голосов
/ 13 января 2010

Я только что нашел это сообщение в блоге Ракеша Раджана , в котором описывается одно из возможных решений:

Переопределите XmlSerialization, заставив тип реализовать класс System.Xml.Serialization.IXmlSerializable. Определите способ сериализации объекта в XML в методе WriteXml и определите способ воссоздания объекта из строки xml в методе ReadXml.

Но это не сработает, поскольку ваш словарь содержит object, а не определенный тип.

...