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

XML 属性包实现

starIconstarIconstarIcon
emptyStarIcon
starIcon
emptyStarIcon

3.32/5 (12投票s)

2002年12月2日

4分钟阅读

viewsIcon

127874

downloadIcon

657

这是 Don Box 出色的 XML 属性包实现的 ATL/COM 移植版本。

引言

首先,我假设您了解 COM。了解序列化的概念会有所帮助,熟悉 IPropertyBagIPersistPropertyBag 接口也会很有益。

这是 Don Box 出色的 XML 属性包实现的移植版本。它是一个实现了 IPropertyBag 接口的 COM 类,这意味着它可以序列化和反序列化实现 IPersistPropertyBag 接口的 COM 对象。

属性包本质上是名称/值对的集合。当对象被序列化时,属性包基本上会要求对象提供其所有属性及其名称。反之,当对象被反序列化时,属性包会要求对象使用属性包中可用的信息来重新填充其属性。

这种信息交换通过 IPropertyBagIPersistPropertyBag 接口完成。属性包实现 IPropertyBag,而要序列化/反序列化的对象实现 IPersistPropertyBag

序列化调用序列可能如下所示

IPropertyBag::SaveToFile("file", pUnkObject);   
     |
     v
IPersistPropertyBag* pPersist;
pUnkObject->QueryInterface(
    IID_IPersistPropertyBag,
    &pPersist);
pPersist->Save(this, ...) --> IPersistPropertyBag::Save(IPropertyBag* pPropBag, ...)
                                                   |
                                                   v
                                         foreach(p in properties) {
IPropertyBag::Write(BSTR name,  <----  pPropBag->Write(p.bstrName, &p.varValue);
                    VARIANT* value)      }
    |
    v
Make data persistant (e.g. save to file)                                  

好的,首先,我为未能绘制一张像样的图表而道歉,因为我没有像样的绘图程序。其次,我想告知您,魔法发生在 IPropertyBag::Write 中。通常,数据在此处被持久化。在此实现中,它会在一个 XML 文档中生成一个 XML 元素,该文档在所有属性都保存完毕后被写入磁盘。

IPropertyBag::Write 可以发挥比看起来更多的魔法。如果传递给它的变体包含一个接口指针,它将查询 IPersistPropertyBag 接口。如果查询得到一个接口指针,它将被递归使用。因此,可以序列化对象树。

反序列化过程与序列化类似。调用的是 IPersistPropertyBag::Read() 而不是 IPersistPropertyBag::Write()。要反序列化的对象调用 IPropertyBag::Read() 而不是 IPropertyBag::Write()。这一点也不难。

IXMLPropertyBag

除了 IPropertyBag,实现的接口还包含另外两个方法

[
    object,
    uuid(FC34FA47-86F7-4B19-88FA-43E073F29E14),
    dual,
    nonextensible,
    helpstring("IXMLPropertyBag Interface"),
    pointer_default(unique)
]
interface IXMLPropertyBag : IDispatch {
    // Serializes an object to an XML file
    [id(1)] HRESULT SaveToFile([in] BSTR bstrFileName, [in] IUnknown* pObject);
    [id(2)] HRESULT LoadFromFile([in] BSTR bstrFileName, 
        [in] IErrorLog* pErrorLog, [out,retval] IUnknown** ppObject);
};

函数 LoadFromFile 不仅接受文件名并返回一个对象,它还接受一个指向错误日志对象的接口指针。当反序列化过程遇到错误时,会使用此日志。

已处理的 VARIANT

此实现可以处理除以下之外的所有变体

  • 即“按引用” - VT_BYREF
  • 即“数组” - VT_ARRAY
  • 无法转换为字符串的 - BSTR

请注意,用户定义类型 VT_RECORD 是支持的,前提是该类型存在类型库信息。

为什么使用我的代码而不是 Don Box 的?

  • 我的代码可以直接编译并运行。您可以获得一个即用型 DLL,而 Don Box 的实现实际上只是一个 CPP 文件,展示了“可以这样实现”。
  • 我相信我使用了更少的资源。Don Box 为每个实现 IPersistPropertyBag 的属性都分配了一个新的属性包。我没有这样做,我使用一个堆栈来存储 XML 元素节点,以便我可以“回溯”。

买者自负

此软件远未完成和完美。以下是您应该注意的一些问题列表

  • 属性名称不能包含任何会在语法上干扰 XML 的内容

    属性名称现在经过处理,使其永远不会干扰 XML 语法。

  • 属性值不能包含任何会在语法上干扰 XML 的内容

    也许我应该注意一下。特殊 XML 字符实际上会在文本中被转义。

  • 在我的编辑器以外的其他编辑器中,源代码看起来很糟糕。

    原因是 Box 先生使用空格进行缩进,而我使用的是真正的制表符。我可能会在某个时候美化代码。

  • 错误日志未使用

    Box 先生从未实现错误日志记录。一旦我找到 IErrorLog 的实现,我很有可能也会实现它。

  • XML 冗长

    XML 可能会大大膨胀您的数据,您对此几乎无能为力。然而,以 XML 形式存储数据的优点是,您可以使用其他工具来操作它,并且可以使用文本编辑器手动编辑它。

许可授予

如果您满足以下条件,您将被授予使用该代码用于任何目的的许可:您不能期望我提供任何保证。如果您弄坏了什么,您自己修复。如果我们有一天能见面,您可以请我喝我选择的啤酒。这不是强制性的,但这样做会非常好,我将非常感激。如果您的宗教禁止您购买任何酒精饮料,即使不是给自己喝,可乐也可以,或者任何您认为在道德和伦理上都可以接受的、令人愉悦的饮料。

另外,请记住,此代码的部分内容是 Don Box 的版权。

顺便说一句,非常感谢 Don Box 先生提供的这段出色的代码。

修订历史

  • 2002-12-02

    初始版本。

  • 2002-12-03

    拼写错误,该死的拼写错误。

  • 2002-12-03

    XML 文本会自动使用 MSXML 和很可能 ActiveDOM 进行转义。因此,对于属性值不需要特别考虑。

  • 2002-12-05

    属性包现在会处理属性名称,使其永远不会破坏 XML 语法。添加了两个新的接口函数:SaveToStreamLoadFromStream。如果您将 XML 属性包保存在结构化存储中,例如,这将非常有用。

© . All rights reserved.