序列化然后反序列化对象,生成与原始对象不同的结果

本文关键字:对象 原始 结果 然后 反序列化 序列化 | 更新日期: 2023-09-27 17:58:43

我会尽力提供我能提供的信息,我不完全确定什么对理解这个问题有帮助。

我注意到我在代码上有一些奇怪的行为,我认为这些行为在功能上应该是相同的。我从会话中提取数据,因为它比每次访问数据库都快,但每当我保存到会话时,我也会保存到数据库(我可以确认这是100%正确的)。

我发现,在某些情况下,从Session中提取数据时会遇到奇怪的行为,但从数据库中提取相同的数据时没有遇到这个问题。

//Good:
string data = DashboardDatabaseRepository.Instance.GetWebLayoutData("PaneStates");
if (!string.IsNullOrEmpty(data))
{
    byte[] dataAsArray = System.Convert.FromBase64String(data);
    MemoryStream stream = new MemoryStream(dataAsArray);
    _paneStates = serializer.Deserialize(stream) as SerializableDictionary<string, RadPaneSetting>;
}
//Bad:
if (!object.Equals(DashboardSessionRepository.Instance.GetSession("PaneStates"), null))
{
    _paneStates = DashboardSessionRepository.Instance.GetSession("PaneStates") as SerializableDictionary<string, RadPaneSetting>;
}

现在,我知道它看起来是什么样子了——我在"GetSession"的幕后做了一些奇怪的事情,但我所做的只是:

public object GetSession( string index )
{
    return HttpContext.Current.Session[index];
}
public void SetSession(string index, object item)
{
    HttpContext.Current.Session[index] = item;
}

现在,事情变得非常奇怪。下面的代码将上面的"坏"代码转换为"好"代码。

//WorkAround - Good
get
{
    SerializableDictionary<string, RadPaneSetting> _paneStates = new SerializableDictionary<string, RadPaneSetting>();
    if (!object.Equals(DashboardSessionRepository.Instance.GetSession("PaneStates"), null))
    {
        _paneStates = DashboardSessionRepository.Instance.GetSession("PaneStates") as SerializableDictionary<string, RadPaneSetting>;
        System.IO.MemoryStream memoryStream = new System.IO.MemoryStream();
        System.Xml.Serialization.XmlSerializer xmlSerializer = new System.Xml.Serialization.XmlSerializer(_paneStates.GetType());
        xmlSerializer.Serialize(memoryStream, _paneStates);
        string data = System.Convert.ToBase64String(memoryStream.ToArray());
        if (!string.IsNullOrEmpty(data))
        {
            XmlSerializer serializer = new XmlSerializer(_paneStates.GetType());
            byte[] dataAsArray = System.Convert.FromBase64String(data);// System.Text.Encoding.UTF8.GetBytes(data);
            MemoryStream stream = new MemoryStream(dataAsArray);
            _paneStates = serializer.Deserialize(stream) as SerializableDictionary<string, RadPaneSetting>;
        }
        else
        {
            XmlSerializer serializer = new XmlSerializer(_paneStates.GetType());
            string data = DashboardDatabaseRepository.Instance.GetWebLayoutData("PaneStates");
            if (!string.IsNullOrEmpty(data))
            {
                byte[] dataAsArray = System.Convert.FromBase64String(data);// System.Text.Encoding.UTF8.GetBytes(data);
                MemoryStream stream = new MemoryStream(dataAsArray);
                _paneStates = serializer.Deserialize(stream) as SerializableDictionary<string, RadPaneSetting>;
            }
            DashboardSessionRepository.Instance.SetSession("PaneStates", _paneStates);
        }
    return _paneStates;
}

我很好奇通过序列化和反序列化从Session中检索到的对象会发生什么样的清理?有没有什么明显的东西让我遗漏了?

谢谢你抽出时间。

编辑:我相信这两本词典有相同的数据。我在S.O.上查找了一个字典平等比较器,并使用了那里提供的代码。注意调试器是如何进入if语句的,但屏幕截图LHS上的对象似乎是相等的。为什么会发生这种情况?

https://i.stack.imgur.com/ojUPm.png

序列化然后反序列化对象,生成与原始对象不同的结果

问题是您正在向后使用UTF-8编码。当你认为你在解码时,你在编码,当你认为自己在编码时,你就在解码。

UTF-8编码不用于将任何二进制数据表示为字符串,而是用于将字符串表示为二进制数据。当您获取任何二进制数据并通过Encoding.UTF8.GetString发送时,可能会损坏数据。它适用于恰好与UTF-8编码的任何字符数据相对应的任何字节码,但一旦获得UTF-8编码数据中不存在的字节组合,该方法就无法将其转换为字符。此外,即使您可以在不丢失数据的情况下将数据转换为字符串,也不能保证在再次对字符串进行编码时会得到相同的二进制结果。

您应该使用base64编码。使用Convert.ToBase64String将二进制数据转换为字符串表示,使用Convert.FromBase64String将二进制数据取回:

string data = Convert.ToBase64String(memoryStream.ToArray());

和:

byte[] dataAsArray = Convert.FromBase64String(data);