Поиск данных в XML с использованием IXMLDocument
Учитывая образец 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;
2 ответа
1. Как исправить выражение XPath?
Любой из них исправит запрос:
1) Добавьте следующую строку после создания домена:
Doc.setProperty('SelectionLanguage', 'XPath');
2) Еще лучше, вы могли бы быть более точным о том, какую версию парсера вы создаете, и замените вашу строку конструктора следующим:
Doc := CoDOMDocument60.Create;
Если запрос ничего не найдет, узел будет пустым.
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;
Этот ответ резюмирует мою запись в блоге австралийской группы пользователей Delphi "Танцы с 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 Как я могу легко добавить элемент типа группы или пользователя?
Для добавления материала лучше всего подойдет библиотека XML с свободно распространяемым API, но вы можете добиться полупроблемы с помощью следующих методов:
Добавить дочерний элемент
Чтобы добавить дочерний элемент, используйте код, подобный этому...
ParentNode.AddChild('child-name')
Это полулегко, потому что это выражение является функцией, которая возвращает IXMLNode.
Добавить атрибут
Чтобы добавить новый атрибут или изменить существующий одноразовый код, как этот...
ElementNode.Attributes['myattrib'] := 'attrib-value'
Не существует нативной идемпотентной версии этой функции, но было бы тривиально накатить свою собственную.
Пример 1
Этот пример примерно повторяет функциональность процедуры OP (), приведенной в вопросе.
// 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;