今天可以开始步入序列化的实质阶段了,定义CSerialization作为序列化的入口(持续更新中)
class EXPORTED_JHMF CSerialization
{
public:
CSerialization();
~CSerialization();
jbool Serialize(CJObject* pObj,CJStream* pStream); //开始序列化对象
CJObject* DeSerialize(CJStream* pStream);
jint32 EngenObjeID(){return ++m_ObjID;}
jint32 EngenObjeTypeID(){return ++m_ObjTypeID;}
jbool CheckTypeCompatible(CJSerializationInfo* pinfs,CJSerializationInfo* sinfo);
void AddObjectSerializationInfos(CJSerializationInfo* pinfs);
jint32 GetInstanceID(CJObject* pObj,jint32 parentID,jint32 findex,jbool bref);
void AddToFix(CHasReadedObject* pReadObj);
CHasReadedObject* AddReadedInstances(CJObject* pObj,jint32 typeID,jint32 id,jint32 parentid);
CHasReadedObject* GetHasReadedObject(jint32 id);
CJSerializationInfo* GetObjectSerializationInfos(CJString name);
CJSerializationInfo* GetObjectSerializationInfos(jint32 typeID);
CJSerializationInfo* GetCurrentObjectSerializationInfos(jint32 typeID,CJObject* pObj);
public:
CJStackT<CJObject*> m_objStack;
static const jint32 topID = 1;
private:
jint32 m_ObjID;
jint32 m_ObjTypeID;
CStringHashTable m_ObjectsInfos;
CJListT<CJSerializationInfo*> m_ObjectSerializationInfos;
CJListT<CTOWriteObject*> m_ObjectInstancesCache;
C2IntHashTable m_WriteObjectInstances;
C2IntHashTable m_ReadedObjectInstances;
C2IntHashTable m_FixingObjects; //等待修复的对象
C2IntHashTable m_CurrentObjectSerializationInfos;//当前对象类型信息(运行时动态库)
};
一步一步来, 先实现序列化的写部分,也就是序列化部分,让后再实现反序列化部分
首先要准备2个映射表,一是对象类型表,也就是保存在hash表m_ObjectsInfos中,另一个是对象实例映射表,保存在hash表m_ObjectsInfos中ObjectInstances
为了提高存储效率,定义了基础数据类型,这种数据类型一旦定义,被序列化后就不能版本兼容了,比如坐标类CJPoint 序列化后如果修改了该类,反序列化的时候就会错位,所以基础数据类型的使用需要特别小心。
对于基本数据类型,比如 int32 CJString 等作为原始数据处理
定义:
#define FIELDTYPE_OBJ 1
#define FIELDTYPE_OBJPTR 2
#define FIELDTYPE_PRIMITIVE 3
#define PRIMITIVETYPE_Invalid 1
#define PRIMITIVETYPE_Boolean 2
#define PRIMITIVETYPE_Byte 3
#define PRIMITIVETYPE_Char 4
#define PRIMITIVETYPE_Currency 5
#define PRIMITIVETYPE_Decimal 6
#define PRIMITIVETYPE_Double 7
#define PRIMITIVETYPE_Int16 8
#define PRIMITIVETYPE_Int32 9
#define PRIMITIVETYPE_Int64 10
#define PRIMITIVETYPE_SByte 11
#define PRIMITIVETYPE_Single 12
#define PRIMITIVETYPE_TimeSpan 13
#define PRIMITIVETYPE_DateTime 14
#define PRIMITIVETYPE_UInt16 15
#define PRIMITIVETYPE_UInt32 16
#define PRIMITIVETYPE_UInt64 17
#define PRIMITIVETYPE_Null 18
#define PRIMITIVETYPE_String 19
#define SER_HRADERTYPE_OBJINFO 1
#define SER_HRADERTYPE_OBJHEADER 2
#define SER_HRADERTYPE_ARRAY 3
#define SER_HRADERTYPE_BASEDATA 4
#define SER_HRADERTYPE_OBJMEMBER 0x10
#define SER_HRADERTYPE__Invalid (PRIMITIVETYPE_Invalid + SER_HRADERTYPE_OBJMEMBER)
#define SER_HRADERTYPE_Boolean (PRIMITIVETYPE_Boolean + SER_HRADERTYPE_OBJMEMBER)
#define SER_HRADERTYPE_Byte (PRIMITIVETYPE_Byte + SER_HRADERTYPE_OBJMEMBER)
#define SER_HRADERTYPE_Char (PRIMITIVETYPE_Char + SER_HRADERTYPE_OBJMEMBER)
#define SER_HRADERTYPE_Currency (PRIMITIVETYPE_Currency + SER_HRADERTYPE_OBJMEMBER)
#define SER_HRADERTYPE_Decimal (PRIMITIVETYPE_Decimal + SER_HRADERTYPE_OBJMEMBER)
#define SER_HRADERTYPE_Double (PRIMITIVETYPE_Double + SER_HRADERTYPE_OBJMEMBER)
#define SER_HRADERTYPE_Int16 (PRIMITIVETYPE_Int16 + SER_HRADERTYPE_OBJMEMBER)
#define SER_HRADERTYPE_Int32 (PRIMITIVETYPE_Int32 + SER_HRADERTYPE_OBJMEMBER)
#define SER_HRADERTYPE_Int64 (PRIMITIVETYPE_Int64 + SER_HRADERTYPE_OBJMEMBER)
#define SER_HRADERTYPE_SByte (PRIMITIVETYPE_SByte + SER_HRADERTYPE_OBJMEMBER)
#define SER_HRADERTYPE_Single (PRIMITIVETYPE_Single + SER_HRADERTYPE_OBJMEMBER)
#define SER_HRADERTYPE_TimeSpan (PRIMITIVETYPE_TimeSpan + SER_HRADERTYPE_OBJMEMBER)
#define SER_HRADERTYPE_DateTime (PRIMITIVETYPE_DateTime + SER_HRADERTYPE_OBJMEMBER)
#define SER_HRADERTYPE_UInt16 (PRIMITIVETYPE_UInt16 + SER_HRADERTYPE_OBJMEMBER)
#define SER_HRADERTYPE_UInt32 (PRIMITIVETYPE_UInt32 + SER_HRADERTYPE_OBJMEMBER)
#define SER_HRADERTYPE_UInt64 (PRIMITIVETYPE_UInt64 + SER_HRADERTYPE_OBJMEMBER)
#define SER_HRADERTYPE_Null (PRIMITIVETYPE_Null + SER_HRADERTYPE_OBJMEMBER)
#define SER_HRADERTYPE_String (PRIMITIVETYPE_String + SER_HRADERTYPE_OBJMEMBER)
#define SER_HRADERTYPE_END 0xFF
好像有点说不清楚了,表达能力还真是不行啊,先不管那么多,定义一个对象写的类,专门写对象
class CObjectWrite
{
public:
CObjectWrite(CSerialization* pSer,CJObject* pObj,jint32 id,CJStream* pStream);
void Write();
void WriteMember();
void WriteObjectInfo(CSerialization::ObjectSerializationInfos* pObjSerinfos);
void WriteObjectHeader();
void WriteObjectEnd();
void WritePrimitive(CJSerializationInfo::SerializationInfoField* pMember);
void WriteBaseDataObject(CJObject* pObj);
private:
CSerialization* m_pSerialization;
CJObject* m_pObj;
CJStream* m_pStream;
CJSerializationInfo m_SerializationInfos;
jint32 m_ObjID;
CSerialization::ObjectSerializationInfos* m_ObjectInfos;
CSerialization::ObjectSerializationInfos* GetObjectSerializationInfos(CJObject* pObj,CJSerializationInfo* pSeris);
};
开始实现序列化
jbool CSerialization::Serialize(CJObject* pObj,CJStream* pStream)
{
if(pObj == JNULL)return FALSE;
//首先,写序列化头
pStream->WriteInt32(0xFF78FF78);
pStream->WriteChar('J');
pStream->WriteChar('H');
pStream->WriteChar('M');
pStream->WriteChar('F');
//写序列化头
//顶层对象ID
m_objStack.Enqueue(pObj);
CJObject* writeObj = JNULL;
while((writeObj = m_objStack.Dequeue()) != JNULL)
{
//将对象写入
if(writeObj->GetType() == JTYPEOF(CTOWriteObject))
{
CTOWriteObject* ptwObj = (CTOWriteObject*)writeObj;
CObjectWrite objwrite(this,ptwObj->m_pObject,ptwObj->m_objID,pStream);
objwrite.Write();
//这时候不能直接删除CTOWriteObject对象,因为在哈希表中还需要继续使用它,所以只能把他放到最后删除
}
else
{
CObjectWrite objwrite(this,writeObj,-1,pStream);
objwrite.Write();
}
}
//结束序列化
pStream->WriteByte(0xFF);
pStream->WriteChar('E');
pStream->WriteChar('N');
pStream->WriteChar('D');
return TRUE;
}
看这代码没什么意思,基本的意思很简单,从(栈)队列中取出一个一个对象,然后调用CObjectWrite 对象把对象写到流中去。简单吧
然后看CObjectWrite 对象的写函数
void CObjectWrite::Write()
{
//首先获取序列化信息
m_pObj->GetObjectData(&m_SerializationInfos);
//获取对象类型信息
m_ObjectInfos = GetObjectSerializationInfos(m_pObj,&m_SerializationInfos);
//开始写对象实例
WriteObjectHeader();
if(m_pObj->GetType().IsArray())
{
//数组
CJObjectArray* pArray = (CJObjectArray*)m_pObj;
m_pStream->WriteByte(SER_HRADERTYPE_ARRAY);
m_pStream->WriteInt32(pArray->GetLength());
for(int i = 0;i < pArray->GetLength();i++)
{
CJObject* pObj = pArray->GetItem(i);
jint32 id = m_pSerialization->GetInstanceID(pObj);
m_pStream->WriteInt32(id);
}
}
else if(m_pObj->GetType().IsSubclassOf(JTYPEOF(CJBaseDataType)))
{
//基础数据类型,直接写
WriteBaseDataObject(m_pObj);
}
else
{
WriteMember();
}
WriteObjectEnd();
}
这个函数也简单,它只关心一个对象的写过程
首先是获取该对象的序列化信息,保存在SerializationInfos中,其次获取对象的类型信息,如果该类型信息不存在,就创建它,并把类型信息写到流里面去
GetObjectSerializationInfos的实现
CSerialization::ObjectSerializationInfos* CObjectWrite::GetObjectSerializationInfos(CJObject* pObj,CJSerializationInfo* pSeris)
{
CJString classname = pObj->GetType().GetName();
CSerialization::ObjectSerializationInfos* objInfos =m_pSerialization-> GetObjectSerializationInfos(classname);
if(objInfos == JNULL)
{
objInfos = new CSerialization::ObjectSerializationInfos();
objInfos->name = classname;
objInfos->count = pSeris == JNULL?0:pSeris->GetStatisticsCount();
objInfos->typeID = m_pSerialization->EngenObjeTypeID();
if(objInfos->count > 0)
{
objInfos->infos = new CSerialization::ObjectSerializationInfo[objInfos->count];
jint32 index = 0;
CJSerializationInfo::SerializationInfoField* tmp = pSeris->m_fields.GetHead();
while(tmp != JNULL)
{
objInfos->infos[index].Name = tmp->name;
objInfos->infos[index].TypeID = tmp->fieldTypeID;
objInfos->infos[index].Valuetypeid = tmp->valuetypeid;
index++;
tmp = pSeris->m_fields.GetNext(tmp);
}
}
//写对象信息
WriteObjectInfo(objInfos);
m_pSerialization->AddObjectSerializationInfos(objInfos);
}
else if(!m_pSerialization->CheckTypeCompatible(objInfos,&m_SerializationInfos))
{
CJString msg;
msg.Format("序列化类型不一致:%s",classname);
JThrowSerialException((LPCTJSTR)msg);
}
return objInfos;
}
然后序列化内容,如果该对象是数组要另外处理,如是基础数据类型就直接写
最后开始写对象的成员类型WriteMember代码如下:
void CObjectWrite::WriteMember()
{
CJSerializationInfo::SerializationInfoField* tmp = m_SerializationInfos.m_fields.GetHead();
while(tmp != JNULL)
{
switch(tmp->fieldTypeID)
{
case FIELDTYPE_OBJ:
if(tmp->value->GetType().IsSubclassOf(JTYPEOF(CJBaseDataType)))
{
//基础数据类型,直接写入
WriteBaseDataObject(tmp->value);
break;
}
case FIELDTYPE_OBJPTR:
//获取该象的ID,如果该对象已经被加入到序列化队列就直接使用它的id,否则创建id并加入到序列化队列中去
{
jint32 id = m_pSerialization->GetInstanceID(tmp->value);
m_pStream->WriteByte(SER_HRADERTYPE_OBJMEMBER);
m_pStream->WriteInt32(id);
}
break;
case FIELDTYPE_PRIMITIVE:
WritePrimitive(tmp);
break;
default:
{
CJString msg;
msg.Format("序列化对象成员类型无法识别:%s.%s",m_ObjectInfos->name,tmp->name);
JThrowSerialException((LPCTJSTR)msg);
}
break;
}
tmp = m_SerializationInfos.m_fields.GetNext(tmp);
}
}
如果成员是对象或者对象指针,就把它加入到对象(栈)队列中去,这里只记录它的ID即可
最后就是写原始类型数据了WritePrimitive
void CObjectWrite::WritePrimitive(CJSerializationInfo::SerializationInfoField* pMember)
{
jbyte vtid = pMember->valuetypeid + SER_HRADERTYPE_OBJMEMBER;
m_pStream->WriteByte(vtid);
switch(vtid)
{
case SER_HRADERTYPE_Boolean:
m_pStream->WriteInt32(*(jbool*)pMember->value);
break;
case SER_HRADERTYPE_Byte:
m_pStream->WriteByte(*(jbyte*)pMember->value);
break;
case SER_HRADERTYPE_Char:
m_pStream->WriteChar(*(char*)pMember->value);
break;
case SER_HRADERTYPE_Double:
m_pStream->WriteDouble(*(double*)pMember->value);
break;
case SER_HRADERTYPE_Int16:
m_pStream->WriteInt16(*(jint16*)pMember->value);
break;
case SER_HRADERTYPE_Int32:
m_pStream->WriteInt32(*(jint32*)pMember->value);
break;
case SER_HRADERTYPE_Int64:
m_pStream->WriteInt64(*(jint64*)pMember->value);
break;
case SER_HRADERTYPE_Single:
m_pStream->WriteFloat(*(float*)pMember->value);
break;
case SER_HRADERTYPE_UInt16:
m_pStream->WriteUInt16(*(juint16*)pMember->value);
break;
case SER_HRADERTYPE_UInt32:
m_pStream->WriteUInt32(*(juint32*)pMember->value);
break;
case SER_HRADERTYPE_UInt64:
m_pStream->WriteUInt64(*(juint64*)pMember->value);
break;
case SER_HRADERTYPE_String:
m_pStream->WriteString(*(CJString*)pMember->value);
break;
case SER_HRADERTYPE_SByte:
case SER_HRADERTYPE_Null:
case SER_HRADERTYPE__Invalid:
case SER_HRADERTYPE_TimeSpan:
case SER_HRADERTYPE_DateTime:
case SER_HRADERTYPE_Currency:
case SER_HRADERTYPE_Decimal:
default:
{
CJString msg;
msg.Format("序列化对象成员基础数据类型无法识别:%s.%s",m_ObjectInfos->name,pMember->name);
JThrowSerialException((LPCTJSTR)msg);
}
break;
}
}
这样,整个序列化过程就算完成了,可能隐患不少,等反序列化过程完成就可以相互验证了。