This answer summarizes my entry on the Australian Delphi User's Group blog, "Dances with XML". Refer to it if you need further detail.
Access to nodes via 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. 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?
Use the 'in' operator with an XPATH expression, and the referenced "Dances with XML" utility unit. 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.
Q2 How can I easily add an item of type group or user?
For adding stuff, an XML library with a fluent API would be best, but you can achieve semi-fluency with the following methods:
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.
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
This example roughly replicates the functionality of the OP's Test() procedure given in the question.
// 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
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. 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;