65.9K
CodeProject 正在变化。 阅读更多。
Home

XML化您的类

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.17/5 (6投票s)

2001 年 1 月 29 日

viewsIcon

249101

downloadIcon

2036

本文介绍如何将一个类序列化为 XML 字符串。

Sample Image - XMLize.gif

引言

您可能已经熟悉使用 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),其中 nnodevarmap 结构的大小。
  • 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_Deptemployee 构造函数中由以下代码创建:

HRESULT hRes = CComObject<CDEPT>::CreateInstance(&m_pDept);
ATLASSERT(SUCCEEDED(hRes));
m_pDept->AddRef();

许可证

本文未附加明确的许可证,但可能在文章文本或下载文件本身中包含使用条款。如有疑问,请通过下面的讨论区联系作者。

作者可能使用的许可证列表可以在此处找到。

© . All rights reserved.