XML化您的类






4.17/5 (6投票s)
2001 年 1 月 29 日

249101

2036
本文介绍如何将一个类序列化为 XML 字符串。
引言
您可能已经熟悉使用 MFC 的 CArchive
类来序列化 C++ 对象。序列化是将对象属性和包含的对象持久化存储的过程,以便通过加载持久化存储的数据来将对象重建到其先前的状态。如果您使用 CArchive
进行序列化,对象将被序列化为二进制形式。
在本文中,我将向您展示如何将一个类序列化为 XML 字符串
以及如何从 XML 字符串
反序列化一个类。在这种情况下,由于我们使用的是 XML,对象将被序列化为文本形式。
使用的平台/技术:ATL,MSXML
为什么要将对象序列化为 XML?
随着 XML 作为在不同系统之间交换数据的格式被广泛使用和接受,将对象序列化为 XML 具有许多优点:
- XML 文档可以轻松地在 Web 上交换。
- 可以在不同的平台上工作。
- 可以使用市场上现有的 XML 解析器,如 Microsoft XML Parser,轻松地解析 XML 树。
现在,我将向您展示如何进行 XML 化。
如何实现?
通用类 CXMLArchive
提供了大部分将类 XML 化(即序列化)和反 XML 化(即反序列化)的功能。只需从 CXMLArchive
派生您的类,并重写纯 virtual
方法以提供特定于类的实现。让我们举个例子看看它是如何工作的。
示例
我们来看一个简单的 employee
对象,看看它如何被序列化为下面所示的模式格式。
员工模式
<?xml version="1.0" ?> <Schema name="Employee-Schema" xmlns="urn:schemas-microsoft-com:xml-data" xmlns:dt="urn:schemas-microsoft-com:datatypes"> <ElementType name="Id" dt:type="int"/> <ElementType name="FirstName" dt:type="string"/> <ElementType name="LastName" dt:type="string"/> <ElementType name="Address" dt:type="string"/> <ElementType name="Employee" model="closed" content="eltOnly"> <element type="Id"/> <element type="FirstName"/> <element type="LastName"/> <element type="Address"/> </ElementType> </Schema>
员工对象
在 employee
对象中,执行以下操作来 XML 化它:
在头文件中
- 包含 XMLArchive.h 文件。
- 将 employee 类从
CXMLArchive
派生。 - 调用
XML_DECLARE_NODE_VAR_MAP(n)
,其中n
是nodevarmap
结构的大小。 - 从
CXMLArchive
类声明以下virtual
函数:InitXMLNodeVariableMap()
FillAttribute()
保存()
在 CPP 文件中
实现虚拟方法。
InitXMLNodeVariableMap()
此方法用于维护一个字典,该字典将 XML 节点与相应的对象变量关联起来,以便 XML 节点的值能够正确地传递给相应的对象变量。将 XML 节点、数据类型和对象变量的指针作为参数传递给 XML_NODE_VAR_MAP
宏。请注意,变体数据类型 (VT_xx
) 用于指定数据类型。
void CEmployee::InitXMLNodeVariableMap()
{
START_XML_NODE_VAR_MAP()
//Specify xml node name, data type and
//the variable to hold the node value
XML_NODE_VAR_MAP(NODE_ID, VT_I4, &m_lId)
XML_NODE_VAR_MAP(NODE_FIRSTNAME, VT_BSTR, &m_szFirstName)
XML_NODE_VAR_MAP(NODE_LASTNAME, VT_BSTR, &m_szLastName)
XML_NODE_VAR_MAP(NODE_ADDRESS, VT_BSTR, &m_szAddress)
END_XML_NODE_VAR_MAP()
}
FillAttribute()
此方法有助于从 XML 树初始化 employee 对象的变量。请注意,所有核心逻辑都在基类中,我们只需要在此处调用基类辅助方法 FillAttributeHelper()
。
BOOL CEmployee::FillAttribute(MSXML::IXMLDOMNode* pXMLNode)
{
ATLASSERT(pXMLNode);
return FillAttributeHelper(pXMLNode,
XML_NODEVARMAP_STRUCT, XML_NODE_VAR_MAP_COUNT);
}
保存()
Save()
方法的目的是构建 XML 树来表示 employee
对象。使用 START_XML, START_XML_ELEMENT
等辅助宏来构建 XML 树。
long CEmployee::Save(MSXML::IXMLDOMNode* pCurrentNode)
{
//Empty DOM tree
EmptyDOM();
//Block of code below will build xml tree
//
XML_SET_CURRENTNODE(pCurrentNode)
START_XML()
START_XML_ELEMENT(NODE_EMPLOYEE)
XML_ELEMENT_LONG(NODE_ID, m_lId)
XML_ELEMENT_STR(NODE_FIRSTNAME, m_szFirstName)
XML_ELEMENT_STR(NODE_LASTNAME, m_szLastName)
XML_ELEMENT_STR(NODE_ADDRESS, m_szAddress)
END_XML_ELEMENT()
END_XML()
return S_OK;
}
XMLize() 方法
XMLize()
和 deXMLize()
是 employee
对象中用于序列化和反序列化的公开方法。调用 Save()
方法来构建 XML 树。一旦 XML 树构建完成,通过调用 IXMLDOMDocument::get_xml()
方法就可以轻松获取 XML 字符串
。
deXMLize() 方法
在 deXMLize()
方法中,只需调用基类方法 LoadXML()
,该方法负责遍历每个 XML 节点并将值从 XML 节点传输到相应的对象变量。
包含的对象
到目前为止一切顺利。如果 employee
对象包含子对象,这是否仍然有效?答案是响亮的“是”。该框架也支持包含对象的 XML 化。您只需要在派生类(在本例中为 employee
)中实现 ReadContainedObject()
。在重写的 ReadContainedObject()
方法中,只需检查节点名称是否是包含对象的名称,如果是,则调用包含对象的 LoadXMLNode()
方法并将 bContainedObjectFound
设置为 TRUE
。
包含对象的示例
如果 employee
对象与 Department
对象有一对一的关联,让我们看看如何使用此框架来 XML 化这种对象层次结构。
部门对象
Dept
对象是一个只有单个属性 name
的简单对象。在 Dept
对象中,实现所有必要的虚拟方法。
InitXMLNodeVariableMap()
void CDept::InitXMLNodeVariableMap()
{
START_XML_NODE_VAR_MAP()
//Specify xml node name, data type and
//the variable to hold the node value
XML_NODE_VAR_MAP("Name", VT_BSTR, &m_szName)
END_XML_NODE_VAR_MAP()
}
FillAttribute()
BOOL CDept::FillAttribute(MSXML::IXMLDOMNode* pXMLNode)
{
ATLASSERT(pXMLNode);
return FillAttributeHelper(pXMLNode,
XML_NODEVARMAP_STRUCT, XML_NODE_VAR_MAP_COUNT);
}
保存()
long CDept::Save(MSXML::IXMLDOMNode* pCurrentNode)
{
//Empty DOM tree
EmptyDOM();
//Block of code below will build xml tree
//
//MSXML::IXMLDOMNodePtr pCurrentNode = NULL;
XML_SET_CURRENTNODE(pCurrentNode)
START_XML()
START_XML_ELEMENT("Dept")
XML_ELEMENT_STR("Name", m_szName)
END_XML_ELEMENT()
END_XML()
return S_OK;
}
XML 化 Dept 对象
一旦 Dept
对象实现了所有虚拟方法,employee
对象在 XML 化时只需调用 Dept
对象的 Save()
方法,在反 XML 化时调用 LoadXMLNode()
方法。
修改后的 employee
对象 Save()
方法现在如下所示:
long CEmployee::Save(MSXML::IXMLDOMNode* pCurrentNode)
{
//Empty DOM tree
EmptyDOM();
//Block of code below will build xml tree
//
//MSXML::IXMLDOMNodePtr pCurrentNode = NULL;
XML_SET_CURRENTNODE(pCurrentNode)
START_XML()
START_XML_ELEMENT(NODE_EMPLOYEE)
XML_ELEMENT_LONG(NODE_ID, m_lId)
XML_ELEMENT_STR(NODE_FIRSTNAME, m_szFirstName)
XML_ELEMENT_STR(NODE_LASTNAME, m_szLastName)
XML_ELEMENT_STR(NODE_ADDRESS, m_szAddress)
m_pDept->Save(XML_GET_CURRENTNODE); //XMLize Dept
END_XML_ELEMENT()
END_XML()
return S_OK;
}
ReadContainedObject()
检查节点名称是否为 Dept
,如果是,则调用 Dept
对象的 LoadXMLNode()
方法来反 XML 化 Dept
对象。
long CEmployee::ReadContainedObject(MSXML::IXMLDOMNode* pNode,
BOOL& bContainedObjectFound)
{
ATLASSERT(pNode);
bContainedObjectFound = FALSE;
BSTR szTempNodeName;
pNode->get_nodeName(&szTempNodeName);
_bstr_t szNodeName(szTempNodeName, FALSE);
if ( szNodeName == _bstr_t("Dept") ) //Load Dept Object
{
bContainedObjectFound = TRUE;
return m_pDept->LoadXMLNode(pNode);
}
return S_OK;
}
您可能想知道 m_pDept
是什么,在哪里创建的?它在 employee.h 中声明为:
CComObject<CDEPT>* m_pDept;
m_Dept
在 employee
构造函数中由以下代码创建:
HRESULT hRes = CComObject<CDEPT>::CreateInstance(&m_pDept);
ATLASSERT(SUCCEEDED(hRes));
m_pDept->AddRef();
许可证
本文未附加明确的许可证,但可能在文章文本或下载文件本身中包含使用条款。如有疑问,请通过下面的讨论区联系作者。
作者可能使用的许可证列表可以在此处找到。