使用IXMLDocument以XML格式搜索数据

时间:2012-08-28 19:40:44

标签: xml delphi delphi-2010 ixmldomdocument

鉴于下面的XML示例;

  1. 如何轻松检查给定对象是否存在?
  2. 如何轻松添加类型组或用户的项目? (添加整个块)
  3. <role>
        <access>
            <control>
                <type>group</type>
                <object>COMPUTER\Administrators</object>
            </control>
            <control>
                <type>user</type>
                <object>COMPUTER\Admin</object>
            </control>
        </access>
    </role>
    

    代码:

    var
      Doc: IXMLDOMDocument2;
      Node: IXMLDOMNode;
    procedure Test;
    begin
      Doc := CreateOleObject('Microsoft.XMLDOM') as IXMLDomDocument2;
      Doc.load('test.xml');
    
      // This Works
      Node := Doc.selectSingleNode('//role/access/control');
    
      // But this does not work:
      Node := Doc.selectSingleNode('//role/access/control[type = ''group'']');
    
      // EDIT: This does work, but how to combine with object=COMPUTER\Admin?
      Node := Doc.selectSingleNode('//role/access/control[type="group"]');
    
      // EDIT: This does not work either
      Node := Doc.selectSingleNode('//role/access/control[type="group" and object="COMPUTER\Administrators"]');
    end;
    

2 个答案:

答案 0 :(得分:4)

1。如何修复XPath表达式?

其中任何一个都将修复查询:

1)创建dom后添加以下行:

  Doc.setProperty('SelectionLanguage', 'XPath');

2)更好的是,您可以更明确地了解要创建的解析器版本,并用此替换构造线:

Doc := CoDOMDocument60.Create; 

如果查询没有找到任何内容,则Node将为空。

if not Assigned(Node) then...

MSXML3解析器的默认查询语言是XSLPatterns。您需要将其显式设置为XPath。已经有一段时间了,因为我必须处理它,但我认为CreateOleObject行必须创建默认的MSXML解析器。

更新:你的问题的后半部分的解决方案是从优雅的TLama无耻地(经许可)偷走的。 :)

2。如何添加“控制”节点?

忽略目标文档格式和错误处理,例如这样:

procedure TForm1.Button2Click(Sender: TObject);
var
  XMLRoot: IXMLDOMNode;
  XMLChild: IXMLDOMNode;
  XMLDocument: IXMLDOMDocument2;
begin
  XMLDocument := CreateOleObject('Microsoft.XMLDOM') as IXMLDomDocument2;
  XMLDocument.load('XMLFile.xml');
  XMLRoot := XMLDocument.selectSingleNode('//role/access');
  if Assigned(XMLRoot) then
  begin
    XMLRoot := XMLRoot.appendChild(XMLDocument.createElement('control'));
    XMLChild := XMLRoot.appendChild(XMLDocument.createElement('type'));
    XMLChild.text := 'user';
    XMLChild := XMLRoot.appendChild(XMLDocument.createElement('object'));
    XMLChild.text := 'COMPUTER\TLama';
    XMLDocument.save('XMLFile.xml');
  end;
end;

答案 1 :(得分:2)

这个答案总结了我在澳大利亚德尔福用户组博客"Dances with XML"上的条目。如果您需要更多细节,请参阅它。

通过XML访问节点

您正朝着正确的方向前进,尝试利用XPATH作为访问和浏览XML文档的简单机制。只是你的实现需要一些抛光。示范代码如下所示。

Q1如何轻松检查给定对象是否存在?

将'in'运算符与XPATH表达式一起使用,并使用引用的“与XML共舞”实用程序单元。例如,使用您提供的输入文档,此代码片段将测试是否 存在带

的控制节点
if 'role/access/control[type="group"]' in XFocus(Root) then
    ShowMessage(' Hello! I''m here.')

...其中Root是文档根节点。

Q2如何轻松添加类型组或用户的项目?

对于添加内容,使用流畅API的XML库最好,但您可以使用以下方法实现半流畅:

添加子元素

要添加子元素,请使用此代码......

ParentNode.AddChild('child-name')

这是半流利的,因为上面的表达式是一个返回IXMLNode的函数。

添加属性

要添加新属性,或更改现有属性,请使用此代码......

ElementNode.Attributes['myattrib'] := 'attrib-value'

此功能没有本机幂等版本,但滚动自己的功能将是微不足道的。

示例1

这个例子粗略地复制了问题中给出的OP的Test()程序的功能。

// uses uXMLUtils from referenced demo.
procedure Test;
begin
  Doc := LoadDocument_MSXML_FromStream( TestXMLStream);
  Root := Doc.Node;

  // To test if control node exists:
  if 'role/access/control' in XFocus(Root) then
    ShowMessage('The control node exists!');

  // To access each control node:
  for ControlNode in 'role/access/control' then
    DoSomethingForEachControlNode( ControlNode);

  // To access on the first control node:
  for ControlNode in '(role/access/control)[1]' then
    DoSomethingForFirstControlNode( ControlNode);

  // To access on the first control node which has BOTH group type and Admin object:
  for ControlNode in '(role/access/control[type="group"][object="COMPUTER\Administrators"])[1]' do
    DoSomething( ControlNode);

  // To do something for EACH control node which is EITHER group type or Admin object:
  for ControlNode in 'role/access/control[type="group" or object="COMPUTER\Administrators"]' do
    DoSomething( ControlNode);

端;

示例2

假设我们要添加一个计算机管理员组,但前提是它还不存在。如果添加,新节点将进入新的访问节点。如果我们利用XPATH,我们可以通过一些微不足道的代码实现这一目标。这显示在下面的代码片段中。

if not 'role/access/control[type[.="group"][object[.="COMPUTER\Administrators"]]' in XFocus(Root) then
  begin
  ControlNode := Root.ChildNodes.FindNode('role')
                      .AddChild(['access')
                       .AddChild('control');
  ControlNode.AddChild('type'  ).Text := 'group';
  ControlNode.AddChild('object').Text := 'COMPUTER\Administrators'
  end;