【问题标题】:Saving a Dictionary<int, object> in C# - Serialization?在 C# 中保存 Dictionary<int, object> - 序列化?
【发布时间】:2016-03-31 12:30:09
【问题描述】:

我有一本 c# 字典

private Dictionary<int, UserSessionInfo> userSessionLookupTable = new Dictionary<int, UserSessionInfo>();

现在我已经创建了一个字典对象

this.userSessionLookupTable.Add(userSessionInfoLogin.SessionId, userSessionInfoLogin);

现在我想要一个通用方法来将字典序列化和反序列化为字节数组。 喜欢

public static void Serialize(Dictionary<int, object> dictionary, Stream stream)
{
 //Code here
}

public static static Dictionary<int, object> Deserialize(Stream stream)
{
 //Code here
}

谁能帮我解决这个问题?

【问题讨论】:

  • 你想怎么序列化呢? JSON?二进制? XML?
  • 二进制..问题已编辑

标签: c# dictionary serialization binary-serialization


【解决方案1】:

试试这个....

public static void Serialize<Object>(Object dictionary, Stream stream)
{
    try // try to serialize the collection to a file
    {
        using (stream)
        {
            // create BinaryFormatter
            BinaryFormatter bin = new BinaryFormatter();
            // serialize the collection (EmployeeList1) to file (stream)
            bin.Serialize(stream, dictionary);
        }
    }
    catch (IOException) { ... }
}

public static Object Deserialize<Object>(Stream stream) where Object : new()
{
    Object ret = CreateInstance<Object>();
    try
    {
        using (stream)
        {
            // create BinaryFormatter
            BinaryFormatter bin = new BinaryFormatter();
            // deserialize the collection (Employee) from file (stream)
            ret = (Object)bin.Deserialize(stream);
        }
    }
    catch (IOException) { ... }
    return ret;
}

// function to create instance of T
public static Object CreateInstance<Object>() where Object : new()
{
    return (Object)Activator.CreateInstance(typeof(Object));
}

用法...

Serialize(userSessionLookupTable, File.Open("data.bin", FileMode.Create));
Dictionary<int, UserSessionInfo> deserializeObject = Deserialize<Dictionary<int, UserSessionInfo>>(File.Open("data.bin", FileMode.Open));

我在上面的代码中使用了“对象”来满足您的要求,但我个人会使用“T”,它通常表示 C# 中的通用对象

【讨论】:

    【解决方案2】:

    您可以为此目的使用MemoryStream,除非您不想创建文件。但是对于序列化方法,如果您不想返回值,您可能应该将stream 参数标记为[Out]

    public static void Serialize(Dictionary<int, object> dictionary, [Out] Stream stream)
    {
        stream = new MemoryStream();
        new BinaryFormatter().Serialize(stream, dictionary);
    }
    
    public static Dictionary<int, object> Deserialize(Stream stream)
    {
        return (Dictionary<int, object>)new BinaryFormatter().Deserialize(stream);
    }
    

    这将执行二进制序列化。

    编辑:

    要将其作为字节数组获取,您只需将从Serialize() 方法返回的流转换为MemoryStream,然后对其调用.ToArray()

    这是一个例子:

    MemoryStream outputStream;
    Serialize(your dictionary here, outputStream);
    byte[] bytes = outputStream.ToArray();
    

    【讨论】:

    • 如果你只是要创建一个新的流,你应该将流标记为 out
    • @MikeT :谢谢,我不确定应该使用refOut 中的哪一个。
    • ref 表示您传递的是对象引用而不是其值,这意味着对对象的更改会反映在传递的成员中(注意类是引用类型,因此始终是 ref),out 表示您是将创建一个对象并从方法中返回它,替换调用函数中最初存在的任何值
    • 我不是本地 C# 开发人员,但我知道 ref 的作用(在 Visual Basic 中相当于 ByRef)。不过,您为它写了一个简单而清晰的解释;)。我有时使用ref 而不是Out 来节省内存(但这只是针对特殊情况)。
    【解决方案3】:

    回答可能有点晚,但根据我的经验,这是一个实用且更通用的答案:如果您想要快速简单的东西,或者对数据大小或性能没有太多要求,请使用简单的 JSON 序列化。如果您还想要空间效率高、速度快且跨平台的东西,请使用 Google Protocol Buffers。

    JSON

    string output = JsonConvert.SerializeObject(myObject);
    var copyOfMyObject = JsonConvert.DeserializeObject<MyObject>(output);
    

    您可以通过多种方式control the serialisation and use streams and so on

    Json.Net also supports dictionaries too.

    Protobuf

    在c#中有两种使用protobuf的方法:Using .proto message definitions and generate codereflection based approach using your class definitions and attributes。这取决于您的项目,但我个人更喜欢消息定义。

    请注意,您可能必须在序列化和反序列化之前和之后进行自己的修改才能与 Dictionary 一起使用。

    基于消息定义:使用proto3

    .proto 文件:

    message Person {
      int32 id = 1;
      string name = 2;
    }
    

    使用Google Protobuf生成代码:

    protoc --csharp_out=$DST_DIR $SRC_DIR/person.proto
    

    Serialise / deserialise using generated classes:

    Person john = ...;
    john.WriteTo(stream);
    var copyOfJohn = Person.Parser.ParseFrom(stream);
    

    另外,还有一个简短的说明,让您知道proto3 is the capability of using JSON 可能有一个鲜为人知的功能。

    基于反射:Protobuf-net

    这是一个简单的例子(复制自protobuf-net page

    [ProtoContract]
    class Person {
        [ProtoMember(1)]
        public int Id {get;set;}
        [ProtoMember(2)]
        public string Name {get;set;}
    }
    Serializer.Serialize(file, person);
    newPerson = Serializer.Deserialize<Person>(file);
    

    二进制、文本、流,天哪!

    抱歉,我有点忽略了关于“二进制”序列化的问题(并且不是特定的BinaryFormatter 问题),因为有很多方法可以使用流来处理流入和流出进程的数据。 C# 流、读取器和写入器以及其他 System.IO APIs 都有很好的文档记录。

    【讨论】:

      【解决方案4】:

      对于二进制序列化

      欲了解更多信息,请查看BinaryFormatter

      这是一个可能的解决方案:

      public void Serialize(Dictionary<int, UserSessionInfo> dictionary, Stream stream)
      {
          BinaryWriter writer = new BinaryWriter(stream);
          writer.Write(dictionary.Count);
          foreach (var obj in dictionary)
          {
              writer.Write(obj.Key);
              writer.Write(obj.Value);
          }
          writer.Flush();
      }
      
      public Dictionary<int, UserSessionInfo> Deserialize(Stream stream)
      {
          BinaryReader reader = new BinaryReader(stream);
          int count = reader.ReadInt32();
          var dictionary = new Dictionary<int, UserSessionInfo>(count);
          for (int n = 0; n < count; n++)
          {
              var key = reader.ReadInt32();
              var value = reader.ReadString();
              dictionary.Add(key, value);
          }
          return dictionary;                
      }
      

      但您仍然需要有 UserSessionInfo ToString() 转换器;


      对于XML序列化

      创建一个示例类 Session

      public class Session
      {
          [XmlAttribute]
          public int SessionID;
          [XmlAttribute]
          public UserSessionInfo SessionInfo;
      }
      

      如果您想将其序列化为XML

      ,则可以创建XmlSerializer
      XmlSerializer serializer = new XmlSerializer(
          typeof(Session[]),
          new XmlRootAttribute() { ElementName = "sessions" }
      );
      

      现在你可以序列化或反序列化了。

      序列化:

      serializer.Serialize(
          stream, 
          dict.Select(kv => new Session(){SessionID = kv.Key, SessionInfo = kv.Info}).ToArray()
      );
      

      反序列化:

      var deserialized = (
          (Session[])serializer.Deserialize(stream)
      ).ToDictionary(i => i.id, i => i.info);
      

      但您需要在 UserSessionInfo 中有 ToString() 方法才能将其存储在 XML 中。

      XML 可能如下所示:

      <sessions>
          <session id='int_goes_here' value='string_goes_here'/>
      </sessions>
      

      希望这会有所帮助。

      【讨论】:

      • 对不起,我想将其序列化为字节数组...问题已编辑
      • 为二进制添加了可能的序列化方法
      【解决方案5】:

      除非您想构建自己的完全自定义的序列化程序,否则您需要使用内置的serialization structure

      字典和所有引用的类型必须实现内置的序列化结构,这需要使用正确的属性来修饰类,即[Serializable][DataContact],并且由于对象无法实现它,因此您无法对其进行序列化直接,你需要把它改成可序列化的类型

      【讨论】:

      • 注意,使用属性通常不叫Implementing,确实存在ISerializable接口(msdn.microsoft.com/en-us/library/…)
      • 你很快就跑题了,我意识到我需要更正术语但被你编辑锁定了
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2011-04-30
      • 2017-06-12
      • 1970-01-01
      • 2016-08-27
      • 1970-01-01
      • 2015-11-07
      • 2021-05-29
      相关资源
      最近更新 更多