【问题标题】:Are Hashtables Serializable?哈希表是可序列化的吗?
【发布时间】:2016-11-16 04:24:57
【问题描述】:

我在互联网上看到了一个普遍的belief(2009 年文章)Hashtable 类不可序列化;但是,我找不到任何支持这一概念的现代文档。

这种信念源于另一个有据可查的信念,即IDictionary 接口会阻止序列化;但是,我今天在 MSDN 中找不到任何支持这种说法的东西。

此外,Hashtable 实现了ISerializable 并包含接受序列化信息的扩展方法。

那么,交易是什么? Hashtable 是可序列化的吗?支持IDictionary这一概念的文档在哪里?

进一步说明请阅读):

大量文档支持IDictionary 不可序列化的说法;但是,这侧重于使用基于 XML 的序列化与类交互。 ISerializable 在下面的 cmets 和 MSDN 中都提到,表明一个类是可序列化的。这也意味着类必须负责自己的序列化。

我认为这否定了 Hashtable 不可序列化的说法。这也许是我的问题的起源。

【问题讨论】:

  • @RobertHarvey 当然。确实如此。但是要说某些东西显然不可序列化应该得到文档的支持,特别是如果这个东西扩展了ISerializable。考虑到 NewtonSoft 等库的可用性,这似乎是一个特别难以支持的声明。
  • 它在this page: "XmlSerializer 无法处理实现 IDictionary 接口的类。这部分是由于调度限制,部分是由于哈希表在XSD 类型系统。唯一的解决方案是实现一个不实现 IDictionary 接口的自定义哈希表。”(Google 是一个很棒的工具)。
  • 根据this article,给出的原因是“抛出 System.NotSupportedException 的原因是因为 IDictionary 类实现了 IXmlSerializable 接口。IXmlSerializable 接口使我们覆盖了以下方法:GetSchema、ReadXml和 WriteXml。”。 2010 年文章 :)
  • 抛开所有关于序列化的讨论——现在是 2016 年,为什么还要使用 HashTable
  • 序列化有多种形式。有些工作(BinaryFormatter,Json),有些不工作(Xml)。这在 2016 年是否相关?根本不要使用Hashtable 类。

标签: c# .net serialization


【解决方案1】:

普遍的信念之所以如此普遍,是因为它是真的:

var t = new Hashtable();
t.Add("Hi!", "I'm here");
t.Add("Hm", "Yup");

var serializer = new XmlSerializer(typeof(Hashtable));

using (var sw = new StringWriter())
{
  serializer.Serialize(sw, t);

  Console.WriteLine(sw.ToString());
}

抛出

NotSupportedException:不支持 System.Collections.Hashtable 类型,因为它实现了 IDictionary。

这并不意味着 实际上不可能序列化哈希表。当然,我可以遍历所有键和值,将它们写入字符串,然后从中重建哈希表。只是我无法完全使用序列化基础设施。

这里的推理是什么?它实际上非常简单 - XmlSerializer 旨在产生良好的 XML,本着 XML 设计的交换格式的精神。而且 XML 没有任何适合的字典或“键值”机制。所以为了支持哈希表序列化,他们必须用自己的规则制作自己的“子格式”。在设计 .NET 时,这是一个巨大的禁忌——XML 是一种 interchange 格式。格式的任何扩展(哈哈)都意味着您不再兼容,无论您有多么好的想法。

当然,如今,每个人和他们的祖母都在生成不用于交换目的的 XML 数据。而且这并不完全是一件坏事(毕竟.NET config 文件也是一种 XML 格式)。但这也有点不对。

相比之下,使用BinaryFormatter 之类的内容。这是 .NET 团队设计整个格式的类,不受标准限制。你瞧 - BinaryFormatter 可以序列化和反序列化 Hashtable 就好了。

因此,稍微更正确的信念是“Hashtable 无法序列化为有效的标准 XML。当您尝试序列化 Hashtable 时,XmlSerializer 类尤其会引发错误。”

【讨论】:

  • 我是一名前 Java 开发人员。我仍然习惯于很多人将“序列化”这个词与XmlSerializer 联系在一起的想法(在此加强)。
  • @Thomas 很有趣,因为当我开始使用 Java 时,每个人都将 Xml 与 Java 中的一切联系起来:P .NET(当然还有 JVM 生态系统)非常复杂,而且它有很多不同的目标。某些对象可以以一种方式序列化但不能以另一种方式序列化,这是一个好主意吗?它肯定会让事情看起来不那么抽象和糟糕的 OOP,不是吗? .NET 主要围绕 Web 服务和 SOAP 进行营销,因此与 XML 的连接很有意义。 XmlSerializer 非常具体,因为它明确设计用于制作好的 XML; BinaryFormatter 有自己的格式。
  • 当我离开 Java 时,我使用的是 Spring 注释(底层是 XML)。我个人不喜欢 XML(无非是厌恶)。但是,作为 Java J2EE(注解再次覆盖 XML),特别是作为 JavaScript 工程师,XML 和序列化(对我而言)并没有那么紧密的联系。也许是不同的世代?
  • @Thomas 好吧,早在我第一次听说 XML 之前,我就一直在使用二进制序列化,包括像“把这整堆内存中的 C 结构并将其保存到一次性归档”。而且我仍然使用二进制序列化来保证它的一切。只是我真的很喜欢 XML——它是一种定义明确的互操作格式,在我的大多数商业项目中,互操作几乎是我们使用序列化的唯一地方(以及一些关键的手工制作的二进制格式)。我怀疑现代程序员会对 JSON 有类似的看法:)
【解决方案2】:

Hashtable 是否实现 ISerializable?绝对:

public class Hashtable : IDictionary, ICollection, IEnumerable, ISerializable, IDeserializationCallback, ICloneable

我们可以将 Hashtable 序列化为 XML 吗?让我们试试吧:

        var hash = new System.Collections.Hashtable();
        hash[7] = "7";
        hash[8] = "8";
        var serializer = new System.Xml.Serialization.XmlSerializer(typeof(System.Collections.Hashtable));
        TextWriter writer = new System.IO.StreamWriter(@"C:\SomeFile.xml");
        serializer.Serialize(writer, hash);

结果...如您所料的错误

System.Xml.dll 中出现“System.NotSupportedException”类型的异常,但未在用户代码中处理

附加信息:不支持 System.Collections.Hashtable 类型,因为它实现了 IDictionary。

因此,在 .Net 4.5+ 中似乎确实如此

但是让我们再尝试一次二进制序列化...

        var hash = new System.Collections.Hashtable();
        hash[7] = "7";
        hash[8] = "8";
        var formatter = new System.Runtime.Serialization.Formatters.Binary.BinaryFormatter();
        Stream stream = new FileStream(@"C:\SomeFolder\SomeFile.bin", FileMode.Create, FileAccess.Write, FileShare.None);
        formatter.Serialize(stream, hash);
        stream.Close();

结果...没有抛出错误...所以问题似乎与 IDictionary 和 XmlSerialization 相关,但并非所有序列化

如果您确实需要对 XML 执行此操作,ManoDestra 有一个不错的链接 https://blogs.msdn.microsoft.com/adam/2010/09/10/how-to-serialize-a-dictionary-or-hashtable-in-c/

另外,有趣的是,XML 序列化提到你不能序列化无符号长整数或集合。

Microsoft XML Serialization (MSDN)

【讨论】:

  • 回答得很好。感谢您的时间和来源。
【解决方案3】:

Microsoft 似乎说这当然是可能的。 https://msdn.microsoft.com/en-us/library/b85344hz(v=vs.110).aspx

using System;
using System.IO;
using System.Collections;
using System.Runtime.Serialization.Formatters.Binary;
using System.Runtime.Serialization;

public class App 
{
    [STAThread]
    static void Main() 
    {
        Serialize();
        Deserialize();
    }

static void Serialize() 
{
    // Create a hashtable of values that will eventually be serialized.
    Hashtable addresses = new Hashtable();
    addresses.Add("Jeff", "123 Main Street, Redmond, WA 98052");
    addresses.Add("Fred", "987 Pine Road, Phila., PA 19116");
    addresses.Add("Mary", "PO Box 112233, Palo Alto, CA 94301");

    // To serialize the hashtable and its key/value pairs,  
    // you must first open a stream for writing. 
    // In this case, use a file stream.
    FileStream fs = new FileStream("DataFile.dat", FileMode.Create);

    // Construct a BinaryFormatter and use it to serialize the data to the stream.
    BinaryFormatter formatter = new BinaryFormatter();
    try 
    {
        formatter.Serialize(fs, addresses);
    }
    catch (SerializationException e) 
    {
        Console.WriteLine("Failed to serialize. Reason: " + e.Message);
        throw;
    }
    finally 
    {
        fs.Close();
    }
}


static void Deserialize() 
{
    // Declare the hashtable reference.
    Hashtable addresses  = null;

    // Open the file containing the data that you want to deserialize.
    FileStream fs = new FileStream("DataFile.dat", FileMode.Open);
    try 
    {
        BinaryFormatter formatter = new BinaryFormatter();

        // Deserialize the hashtable from the file and 
        // assign the reference to the local variable.
        addresses = (Hashtable) formatter.Deserialize(fs);
    }
    catch (SerializationException e) 
    {
        Console.WriteLine("Failed to deserialize. Reason: " + e.Message);
        throw;
    }
    finally 
    {
        fs.Close();
    }

    // To prove that the table deserialized correctly, 
    // display the key/value pairs.
    foreach (DictionaryEntry de in addresses) 
    {
        Console.WriteLine("{0} lives at {1}.", de.Key, de.Value);
    }
}
}

【讨论】:

    猜你喜欢
    • 2013-04-17
    • 1970-01-01
    • 1970-01-01
    • 2015-03-12
    • 2013-06-25
    • 2015-02-13
    • 2021-10-12
    • 2013-03-16
    • 2022-01-05
    相关资源
    最近更新 更多