简体   繁体   English

使用IXMLDocument以XML格式搜索数据

[英]Search data in XML using IXMLDocument

Given the XML sample below; 鉴于下面的XML示例;

  1. How can I easily check if a given object exists? 如何轻松检查给定对象是否存在?
  2. How can I easily add an item of type group or user? 如何轻松添加类型组或用户的项目? (add a whole block) (添加一整块)

<role>
    <access>
        <control>
            <type>group</type>
            <object>COMPUTER\Administrators</object>
        </control>
        <control>
            <type>user</type>
            <object>COMPUTER\Admin</object>
        </control>
    </access>
</role>

Code: 码:

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;

1. How to fix the XPath expression ? 1.如何修复XPath表达式?

Either of these will fix the query: 以下任何一个都将修复查询:

1) Add the following line after creating the dom: 1)创建dom后添加以下行:

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

2) Better yet, you could be more explicit about which version of the parser you are creating and replace your construction line with this: 2)更好的是,您可以更明确地了解要创建的解析器版本,并使用以下内容替换构造线:

Doc := CoDOMDocument60.Create; 

If the query doesn't find anything, Node will be empty. 如果查询没有找到任何内容,则Node将为空。

if not Assigned(Node) then...

The default query language for the MSXML3 parser is XSLPatterns. MSXML3解析器的默认查询语言是XSLPatterns。 You needed to explicitly set it to XPath. 您需要将其显式设置为XPath。 It's been a while since I've had to deal with it, but I assume the CreateOleObject line must create the MSXML parser my default. 已经有一段时间了,因为我必须处理它,但我认为CreateOleObject行必须创建我的默认MSXML解析器。

Update: Solution for the second half of your question stolen shamelessly (with permission) from the gracious TLama. 更新:你的问题的后半部分的解决方案是从优雅的TLama无耻地(经许可)偷走的。 :) :)

2. How to add a "control" node ? 2.如何添加“控制”节点?

Ignoring target document formatting and error handling eg this way: 忽略目标文档格式和错误处理,例如这样:

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;

This answer summarizes my entry on the Australian Delphi User's Group blog, "Dances with XML" . 这个答案总结了我在澳大利亚Delphi用户组博客“与XML共舞”中的条目。 Refer to it if you need further detail. 如果您需要更多细节,请参阅它。

Access to nodes via XML 通过XML访问节点

You are headed in the right direction, by trying to leverage XPATH as an easy mechanism to access and navigate through an XML document. 通过尝试利用XPATH作为访问和浏览XML文档的简单机制,您将朝着正确的方向前进。 It just that your implementation needs a bit of polishing. 只是你的实现需要一些抛光。 Demonstrative code is shown below. 示范代码如下所示。

Q1 How can I easily check if a given object exists? Q1如何轻松检查给定对象是否存在?

Use the 'in' operator with an XPATH expression, and the referenced "Dances with XML" utility unit. 使用带有XPATH表达式的“in”运算符和引用的“Dances with XML”实用程序单元。 For example with your supplied input document, this code fragment tests if a control node exists with a 例如,使用您提供的输入文档,此代码片段将测试控制节点是否存在

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

...where Root is the document root node. ...其中Root是文档根节点。

Q2 How can I easily add an item of type group or user? Q2如何轻松添加组或用户类型的项目?

For adding stuff, an XML library with a fluent API would be best, but you can achieve semi-fluency with the following methods: 对于添加内容,使用具有流畅API的XML库是最好的,但您可以使用以下方法实现半流畅性:

Add a child element 添加子元素

To add a child element use code like this... 要添加子元素,请使用以下代码...

ParentNode.AddChild('child-name')

This is semi-fluent because this above expression is a function which returns IXMLNode. 这是半流利的,因为上面的表达式是一个返回IXMLNode的函数。

Add an attribute 添加属性

To add a new attribute, or change an existing one use code like this... 要添加新属性,或更改现有属性,请使用以下代码...

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

There is no native idempotent version of this functionality, but it would be trivial to roll your own. 此功能没有本机幂等版本,但滚动自己的功能将是微不足道的。

Example 1 例1

This example roughly replicates the functionality of the OP's Test() procedure given in the question. 这个例子粗略地复制了问题中给出的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);

end; 结束;

Example 2 例2

Let's say we want to add a computer administrators group, but only if one does not already exist. 假设我们想要添加一个计算机管理员组,但前提是它还不存在。 If adding, the new nodes go under a new access node. 如果添加,新节点将进入新的访问节点。 We can achieve this with a trivial amount of code if we leverage XPATH. 如果我们利用XPATH,我们可以通过一些微不足道的代码实现这一目标。 This is shown in the code fragment below. 这显示在下面的代码片段中。

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;

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM