如何使用 Dictionary<string,object> 属性序列化对象?

Posted

技术标签:

【中文标题】如何使用 Dictionary<string,object> 属性序列化对象?【英文标题】:How can I serialize an object with a Dictionary<string,object> property? 【发布时间】:2011-01-04 14:15:08 【问题描述】:

在下面的示例代码中,我得到了这个错误

元素 TestSerializeDictionary123.Customer.CustomProperties 类型 System.Collections.Generic.Dictionary`2[[System.String, mscorlib,版本=2.0.0.0, 文化=中性, PublicKeyToken=b77a5c561934e089],[System.Object, mscorlib,版本=2.0.0.0, 文化=中性, PublicKeyToken=b77a5c561934e089]] 可以 不被序列化,因为它 实现 IDictionary。

当我取出 Dictionary 属性时,它很好工作。

如何使用字典属性序列化这个 Customer 对象?或者我可以使用可序列化的 Dictionary 替换类型吗?

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

    

【问题讨论】:

【参考方案1】:

在我们的应用程序中,我们最终使用了:

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

代替:

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

解决了这个问题,因为 DatacontractSerializer 支持字典。

另一个解决方案是XML Serializable Generic Dictionary 解决方法也适用于上述示例,并且使用它的人在该链接上进行了长时间的讨论,可能对处理此问题的人有用。

【讨论】:

虽然 DataContractSerializer 是一个很好的建议,但它并不能涵盖所有场景,而且绝对不能提供与 XmlSerializer 相同级别的粒度和定义。【参考方案2】:

这是一个知道如何序列化自身的通用字典类:

  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);
    
  

【讨论】:

这并不能解决字典的值是object的问题。【参考方案3】:

你不能(除非你自己做,这太可怕了); xml 序列化程序不知道如何处理object,因为它不包含有线格式的类型元数据。一个(hacky)选项是将这些都作为字符串流式传输以进行序列化,但是您需要编写大量额外的解析(等)代码。

【讨论】:

我将不得不对你投反对票,因为这表明它无法完成。它源于将这个问题解释为只想要 XML 序列化,但尽管如此,我觉得我应该尽我的一份力量来防止可能的错误信息 :) 希望 OP 能够澄清他是否只想要 XML 序列化,或者你的帖子可以相应调整。 xml-serialization 标签在问题上,表明 OP 对 XML 序列化感兴趣。【参考方案4】:

您可以改用Binary serialization。 (只要确保你所有的类都标记为[Serializable]。当然,它不会是 XML 格式,但你没有将其列为要求:)

【讨论】:

【参考方案5】:

我刚刚发现了这个blog post by Rakesh Rajan,它描述了一种可能的解决方案:

通过使类型实现 System.Xml.Serialization.IXmlSerializable 类来覆盖 XmlSerialization。在 WriteXml 方法中定义您希望如何在 XML 中序列化对象,并在 ReadXml 方法中定义如何从 xml 字符串重新创建对象。

但这不起作用,因为您的字典包含 object 而不是特定类型。

【讨论】:

【参考方案6】:

如何将 Customer 类标记为 DataContract 并将其属性标记为 DataMembers。 DataContract 序列化程序将为您进行序列化。

【讨论】:

【参考方案7】:

尝试通过 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)
        
            ;
        
    

【讨论】:

以上是关于如何使用 Dictionary<string,object> 属性序列化对象?的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 Dictionary<string,object> 属性序列化对象?

如何从不使用 XElement 的自定义 XML 序列化/反序列化为 `Dictionary<int, string>`?

如何在 UITableViewCell 中查询 (String, Dictionary<String, String>)?

如何使用 System.Text.Json 将 json 字符串或流反序列化到 Dictionary<string,string>

如何将 Optional<Dictionary<String, Any>> 转换为 Dictionary<String, Any> 以发送带有 json 参数的 A

如何将 Dictionary<string,string> 添加到现有 JSON.Net 的 JObject?