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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/2056098/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-06 23:05:03  来源:igfitidea点击:

How can I serialize an object with a Dictionary<string,object> property?

c#serializationdictionaryxml-serialization

提问by Edward Tanguay

In the example code below, I get this error:

在下面的示例代码中,我收到此错误

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]] can not be serialized because it implements IDictionary.

元素 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, mscorlib .0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]] 无法序列化,因为它实现了 IDictionary。

When I take out the Dictionary property, it works fine.

当我取出 Dictionary 属性时,它工作正常

How can I serialize this Customer object with the dictionary property? Or what replacement type for Dictionary can I use that would be serializable?

如何使用字典属性序列化这个 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 = "Hyman", 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 + ")";
        }

    }
}

采纳答案by Edward Tanguay

In our application we ended up using:

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

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

instead of:

代替:

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

which solved the problem as DatacontractSerializer supports Dictionary.

由于 DatacontractSerializer 支持字典,因此解决了这个问题。

Another solution is ths XML Serializable Generic Dictionaryworkaround also works in the above example, and there is a long discussion at that link from people using it, might be useful for people working with this issue.

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

回答by ChrisF

I've just found this blog post by Rakesh Rajanwhich describes one possible solution:

我刚刚找到了 Rakesh Rajan 的这篇博客文章,它描述了一种可能的解决方案:

Override XmlSerialization by making the type implement the System.Xml.Serialization.IXmlSerializable class. Define how you want the object to be serialized in XML in the WriteXml method, and define how you could recreate the object from an xml string in the ReadXml method.

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

But this wouldn't work as your Dictionary contains an objectrather than a specific type.

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

回答by Marc Gravell

You can't (short of doing it all yourself, which is horrible); the xml serializer isn't going to have a clue what to do with object, as it doesn't include type metadata in the wire format. One (hacky) option would be to stream these all as strings for the purposes of serialization, but then you have a lot of extra parsing (etc) code to write.

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

回答by kosto

What about to mark Customer class as DataContract and its properties as DataMembers. DataContract serializer will do the serialization for you.

将 Customer 类标记为 DataContract 并将其属性标记为 DataMembers 怎么样?DataContract 序列化程序将为您进行序列化。

回答by Noon Silk

You can use Binary serializationinstead. (Just make sure all your classes are marked as [Serializable]. Of course, it won't be in XML format, but you didn't list that as a requirement :)

您可以改用二进制序列化。(只需确保您的所有类都标记为[Serializable]。当然,它不会采用 XML 格式,但您没有将其列为要求:)

回答by Hans Passant

Here's a generic dictionary class that knows how to serialize itself:

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

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

回答by Snehal

Try Serializating through BinaryFormatter

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