鉴于下面的XML示例;
<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;
答案 0 :(得分:4)
其中任何一个都将修复查询:
1)创建dom后添加以下行:
Doc.setProperty('SelectionLanguage', 'XPath');
2)更好的是,您可以更明确地了解要创建的解析器版本,并用此替换构造线:
Doc := CoDOMDocument60.Create;
如果查询没有找到任何内容,则Node将为空。
if not Assigned(Node) then...
MSXML3解析器的默认查询语言是XSLPatterns。您需要将其显式设置为XPath。已经有一段时间了,因为我必须处理它,但我认为CreateOleObject行必须创建默认的MSXML解析器。
更新:你的问题的后半部分的解决方案是从优雅的TLama无耻地(经许可)偷走的。 :)
忽略目标文档格式和错误处理,例如这样:
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"上的条目。如果您需要更多细节,请参阅它。
您正朝着正确的方向前进,尝试利用XPATH作为访问和浏览XML文档的简单机制。只是你的实现需要一些抛光。示范代码如下所示。
将'in'运算符与XPATH表达式一起使用,并使用引用的“与XML共舞”实用程序单元。例如,使用您提供的输入文档,此代码片段将测试是否 存在带
的控制节点if 'role/access/control[type="group"]' in XFocus(Root) then
ShowMessage(' Hello! I''m here.')
...其中Root是文档根节点。
对于添加内容,使用流畅API的XML库最好,但您可以使用以下方法实现半流畅:
要添加子元素,请使用此代码......
ParentNode.AddChild('child-name')
这是半流利的,因为上面的表达式是一个返回IXMLNode的函数。
要添加新属性,或更改现有属性,请使用此代码......
ElementNode.Attributes['myattrib'] := 'attrib-value'
此功能没有本机幂等版本,但滚动自己的功能将是微不足道的。
这个例子粗略地复制了问题中给出的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);
端;
假设我们要添加一个计算机管理员组,但前提是它还不存在。如果添加,新节点将进入新的访问节点。如果我们利用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;