从多个源和不同模式序列化对象的更顺畅方法?

Posted

技术标签:

【中文标题】从多个源和不同模式序列化对象的更顺畅方法?【英文标题】:Smoother way to Serialize the Objects from muliple Source and different Schema? 【发布时间】:2016-08-25 14:04:47 【问题描述】:

我正在尝试为我的对象组成一个序列化,它可以来自多个源,比如源 1 和源 2,模式处理在两者中都非常糟糕。所以我试图处理一个平滑的序列化。

void CSerFoo::Serialize(CArchive& ar)

    try
    
        //ar.Flush();
        SerializeEx(ar); // For the Serialization of the Good Ones, from the point now on
    
    catch(...) // If the Object cannot be Serialized using the above Method try the Next way to decode
    
        try
        
            //ar.Flush();
            SpecialSerialize1(ar); // For the Serialization of the Objects from the Source 1, actually code from the Source 1
        
        catch(...) // If the Object cannot be Serialized using the above Method try the Last way to decode
        
            try
            
                //ar.Flush();
                SpecialSerialize2(ar); // For the Serialization of the Objects from the Source 2, actually code from the Source 2
            
            catch(...)
            
                // No way
            
        
    

编辑 1:

这是来自源代码 1 的序列化代码

// From Source 1
IMPLEMENT_SERIAL( CSerFoo, CObject, VERSIONABLE_SCHEMA | 3)
void CSerFoo::Serialize(CArchive& ar) // This will be the Consolidated CSerFoo's SpecialSerialize1

    UINT uiSchema = ar.GetObjectSchema();
    if (ar.IsStoring())
    
        ar << m_sName;                          
        ar << m_sDesc;
        ar << m_String1;

        ar << m_fValue1;
        ar << m_fValue2;

        ar << m_iValue1;
        ar << m_iValue2;

        ar << m_String2;
        ar << m_String3;

    
    else
    
        ar >> m_sName;                          
        ar >> m_sDesc;
        ar >> m_String1;

        if(uischema > 0)// Added in VERSION_SCHEMA 1
        
            ar >> m_fValue1;
            ar >> m_fValue2;
        

        if(uischema > 1) // Added in VERSION_SCHEMA 2
        
            ar >> m_iValue1;
            ar >> m_iValue2;
            ar >> m_String4;
        

        if(uischema > 2) // Added in VERSION_SCHEMA 3
        
            ar >> m_String2;
            ar >> m_String3;
        
    

这是来自源代码 2 的序列化代码

// From Source 2
IMPLEMENT_SERIAL( CSerFoo, CObject, VERSIONABLE_SCHEMA | 3)
void CSerFoo::Serialize(CArchive& ar) // This will be the Consolidated CSerFoo's SpecialSerialize2

    UINT uiSchema = ar.GetObjectSchema();
    if (ar.IsStoring())
    
        ar << m_sName;                          
        ar << m_sDesc;
        ar << m_String1;

        ar << m_fValue1;

        ar << m_fValue2;
        ar << m_iValue1;
        ar << m_iValue2;

        ar << m_iValue3;
        ar << m_String2;
        ar << m_String3;

    
    else
    
        ar >> m_sName;                          
        ar >> m_sDesc;
        ar >> m_String1;

        if(uischema > 0)// Added in VERSION_SCHEMA 1
        
            ar >> m_fValue1;

        

        if(uischema > 1) // Added in VERSION_SCHEMA 2
        
            ar >> m_fValue2;
            ar >> m_iValue1;
            ar >> m_iValue2;
        

        if(uischema > 2) // Added in VERSION_SCHEMA 3
        
            ar >> m_iValue3;
            ar >> m_String2;
            ar >> m_String3;
        
    
    // m_String4 is not there in the Source 2

现在合并的CSerFoo(从现在开始)具有来自两个源的所有字段,我们希望对同一 CSerFoo 对象进行更新的序列化。我们不能在创建另一个类上妥协。

我面临的问题是,在随后的序列化调用中,光标(CArchive::m_lpBufCur)被移动,因此下一次序列化尝试失败。

有没有办法做到这一点?

我错过了什么吗?

提前致谢!

【问题讨论】:

在不知道 SerializeEx 和 SpecialSerializeX 做什么的情况下,这很难说。 这只是对 int、float 和 string 的序列化。每个序列化的序列和字段数可能不同,这就是问题所在。 没有问题,当使用可版本化模式时,可版本化模式是您发明的问题的标准解决方案。简介见CArchive::GetObjectSchema,详情见TN002: Persistent Object Data Format。无论如何,所提出的问题是无法回答的。请提供minimal reproducible example,以便我们有一个共同的基础。 没有干净的方法可以解决您自己编写的混乱局面。 (我什至不知道您是如何说服链接器接受具有相同名称的两个对象CSerFoo。)您必须重写整个序列化基础架构才能重新开始工作。即便如此,解决您违反单一定义规则的问题也是相当复杂的。由于 MFC 将类名存储到序列化流中,因此 SDR 也适用于 MFC 的序列化流。您应该聘请开发人员... 我不建议这样做,但每个 CArchive (CArchive::GetFile()) 中都有一个 CFile。 CFile 有 GetPosition() 和 Seek() 成员。您还需要修复异常处理。 MFC 异常不会自动清除(请参阅Exception Handling in MFC)。 【参考方案1】:

如果您没有strong exepction safety guarantee,异常似乎是解决此问题的错误方法。

因此您可以实现函数,使其遵守它(仅在函数中使用局部偏移,而不是像 CArchive 对象内部的偏移或在函数内部复制它)

但似乎异常在这里被滥用为控制结构。您也许应该考虑仅在“异常”情况下使用异常,并具有一些功能,例如

if(isFormat1Convertible(ar))

    SerializeEx(ar);

else if(isFormat2Convertible(ar))

    SpecialSerialize1(ar);

...

在我看来,这更像是更简洁的代码,并且不会“滥用”异常。

【讨论】:

一个不明白的人写的粗鲁的东西Serialization in MFC。至于异常,应该在函数未能兑现承诺时抛出。这是否致命(或“异常”)由客户端代码决定。一般来说,抛出异常的代码几乎不知道这种失败在上下文中是否是致命的。 是的,这不是“MFC”方式,但我更喜欢“进入”一种语言/框架而不是“进入”一种语言(请参阅“代码完成”第 4 章了解关于这)。并且在互联网/书籍上进行了一些讨论,将 OP 中异常的这种用法视为“代码气味”(并且 OP 存在一些问题,否则该帖子将不存在)。我有一段时间没有使用 MFC,但正如我从文档中看到的那样,有一个叫做“VERSIONABLE_SCHEMA”的东西。也许这可以用来选择正确的格式而不是异常(或其他方法,但这取决于具体情况)。 您必须了解一个框架才能做出明智的决定,无论您是想在其中编程还是在其中编程。 Code Complete 的建议带有理由。您缺乏洞察力使您无法应用它。再说了,你不能编程进MFC的序列化,除非你改了,打破了规则,然后来这里寻求帮助解决你搞砸的烂摊子。 是的,你就在那儿。这主要是对不同设计的建议。要创建“强异常安全”,只需重新打开文件并为每个调用创建一个新的 CArchive 对象就足够了(这在我的回答的第一部分中有所暗示)。或者,如果它来自另一个缓冲区,请在将其加载到 CArchive 之前复制该缓冲区。虽然效率不高。

以上是关于从多个源和不同模式序列化对象的更顺畅方法?的主要内容,如果未能解决你的问题,请参考以下文章

DataTable 合并多个模式

Java传输对象模式

将复杂的 NSObject 序列化为 JSON 模式

待整理笔记(表单序列化操作多态函数对象的状态队列管理模式)

如何在 c# 中使用 JSON 使用 foreach 序列化多个项目?

IO中的装饰器模式