среда, 14 января 2009 г.

Работа с XML

Несмотря на то, что тема работы с XML в Delphi довольно широко обсуждалась в Интернете, вопросы на эту тему довольно часто возникают на всевозможных форумах.

Я тоже уже писал по этому поводу, но хотел бы вернуться к реальному случаю быстрого разбора XML файла и извлечения данных, который я сегодня проделал на работе. Получение необходимых данных у меня заняло не более 5 минут времени.

Предыстория. Сегодня понадобилось обработать данные об установленных программах на компьютерах пользователей (да, да, пиратов выявляем :)). Технический отдел предоставил мне такую информацию содраную с ничего не подозревающих пользователей по сети с использованием WMI. Программа, которой они пользовались выдает отчеты в формате XML. Соответственно - мне притащили гору XML файлов с довольно сложной структурой из которых мне необходимо было вытащить только название установленных программных продуктов.

Обработка. Просмотрев пару файлов вручную, понял что так и состариться не долго, и решил написать небольшой конвертер. Запустив Delphi - выбрал в репозитарии объект XML DataBinding и скормил ему один из файлов. Все настройки и параметры я оставил по умолчанию и в результате у меня сформировался модуль с большим количеством классов и интерфейсов для доступа к элементам этого XML файла. Я не стал долго разбираться со структурой классов, сразу же перешел к написанию конвертера.

В новом консольном приложении написал довольно простой код:


program XML2TXT;

uses
Forms,
Classes, SysUtils,
SoftwareXML in 'SoftwareXML.pas';

procedure CovertXML2Text;
var
softbase : IXMLSTDSoftwareType;
i: integer;
sr: TSearchRec;
CurDir: string;
ExportFile: TStringList;
begin
CurDir := IncludeTrailingPathDelimiter(ExtractFilePath(Application.ExeName));
if FindFirst(CurDir+'*.xml', faAnyFile, sr) = 0 then
repeat
ExportFile := TStringList.Create;
softbase := LoadSTDSoftware(Pchar(CurDir+sr.Name));
for i := 0 to softbase.InstalledSoftware.source.software.Count - 1 do
ExportFile.Add(softbase.InstalledSoftware.source.software[i].DisplayName);
ExportFile.Sort;
ExportFile.SaveToFile(CurDir + softbase.InstalledSoftware.Source.servername+'.txt');
ExportFile.Free;
until FindNext(sr) <> 0;
end;

begin
Application.Initialize;
CovertXML2Text;
end.

В результате которого у меня образовался по одному текстовичку на каждый компьютер в сетке, содержащий список установленного ПО.

Чувствую что данный код потребует пояснений. Например, зачем я в консольном приложении использовал модуль Forms и вызывал процедуру Application.Initialize;?

На самом деле все просто - это небольшой хак, позволяющий использовать XML Data Binding в консольном приложении. Потому как в нем упорно отказывался инициализироваться класс для работы с XML. В истинных причинах пока не разбирался - сегодня было важно время, я и так 4 из 5 минут потратил на борьбу с этой ошибкой. :) Думаю позже разобраться с этой проблемой и написать в чем истинная причина.

Странный класс softbase был создан на основе XML файла - так назывался корневой элемент, а softbase.InstalledSoftware.source.software[i].DisplayName - просто навигация по вложенным элементам до нужного и получение его значения.

Вот собственно так выглядит один из самых быстрых способов работы с XML в Delphi.

5 комментариев:

  1. "...хотел бы вернуться к реальному случаю быстрого разбора XML файла и извлечения данных, который я сегодня проделал на работе. Получение необходимых данных у меня заняло не более 5 минут времени..."

    Есть БД в XML. Размер около 30 мб. Нужно разобрать по запчастям. Насколько быстрым будет процесс? Час, два,.. десять?.. А оно вообще вывезет такой объем?

    ОтветитьУдалить
  2. Если отдавать парсеру этот файл полностью - то есть шанс, что этот процесс будет вечным :)

    Можно поступить так: вычленить из файла структуру документа, и уже этот файл со структурой анализировать - получится в разы быстрее и эффективнее. Тем более что эта функция в первую очередь предназначена для генерации иерархии классов из схемы документа, а остальное - так, дополнительная возможность.

    ОтветитьУдалить
  3. Перед использованием любой функции СОМ необходимо инициализоровать библиотеку СОМ. Модуль Form неявно делает вызов этих методов из метода Application.Initialize. В консольных программах это нужно делать самому:

    initialization
    CoInitialize(nil);
    finalization
    CoUnInitialize;

    ОтветитьУдалить
  4. А где функция "LoadSTDSoftware"? Самое интересное то в ней! У меня она не генериться сама и я не могу загрузить данные :(

    ОтветитьУдалить
  5. [cite]Kozer пишет:
    13 ноября 2009 г. 21:53

    Перед использованием любой функции СОМ необходимо инициализоровать библиотеку СОМ. Модуль Form неявно делает вызов этих методов из метода Application.Initialize. В консольных программах это нужно делать самому:

    initialization
    CoInitialize(nil);
    finalization
    CoUnInitialize;[/cite]
    Можно и проще сделать.
    в uses добавляем вот этот модуль OleAuto
    а в папку с программой вот эти три файлика:
    Ole2.pas
    OleAuto.pas
    OleCtl.pas
    По идее файлы наверное и сами найдутся если Делфи одна на компе, просто у меня две версии и при компиляции берутся dcu от другой версии :)
    А да OleAuto, вроде как должен быть объявлен первым в списке. :)
    Все. OLE грузится автоматом.

    ОтветитьУдалить