Какой самый быстрый парсер XML доступен для Delphi?

29

У нас есть достаточно большие строки XML, которые мы в настоящее время анализируем с помощью MSXML2

Я только что попытался использовать MSXML6, надеясь на улучшение скорости и ничего не получив!

В настоящее время мы создаем много документов DOM, и, я думаю, могут быть некоторые накладные расходы при постоянном взаимодействии с dll MSXML2/6

Кто-нибудь знает о лучшем/более быстром XML-компоненте для Delphi?

Если кто-то может предложить альтернативу, и это быстрее, мы бы хотели интегрировать его, но это было бы очень много, поэтому, надеюсь, структура не будет слишком отличаться от используемой MSXML

Мы используем Delphi 2010

Пол

  • 0
    Я вижу, что мод удалил комментарии, которые содержали ценную информацию. Зачем?
  • 0
    @DavidHeffernan, была ли еще полезная информация, которая не была частью каких-либо ответов? Потоки длинных комментариев всегда подлежат удалению, если модератор-рецензент не видит значение.
Показать ещё 2 комментария
Теги:
delphi-2010

5 ответов

11
Лучший ответ

Недавно у меня была аналогичная проблема, при которой использование синтаксического анализатора MSXML DOM оказалось слишком медленным для данной задачи. Мне пришлось разбирать довольно большие документы > 1 МБ, а потребление памяти анализатора DOM было непомерно высоким. Мое решение состояло в том, чтобы вообще не использовать парсер DOM, но идти с парсером MSXML SAX, управляемым событиями. Это оказалось намного быстрее. К сожалению, модель программирования совершенно другая, но в зависимости от задачи она может стоить того. Крейг Мерфи опубликовал замечательную статью о том, как использовать парсер MSXML SAX в delphi: SAX, Delphi и Ex Em El

  • 1
    В случае такого довольно трудоемкого изменения, и если проверка не требуется, возможно, XmlLite подойдет . См. Раздел «Сценарии использования» XmlLite в разделе « Введение в XmlLite».
  • 0
    @Lars, ссылка "SAX, Delphi и Ex Em El" мертва; (
Показать ещё 1 комментарий
33

некоторое время назад мне пришлось сериализовать record в формате XML; для ex:

 TTest = record
    a : integer;
    b : real; 
 end;

в

    <Data>
        <a type="tkInteger">value</a>
        <b type="tkFloat">value</b>
    </Data>

Я использовал RTTI для рекурсивного перемещения по полям записи и хранения значений в XML. Я пробовал несколько XML-парсеров. Мне не нужна модель DOM для создания xml, но она нужна для ее загрузки.

XML содержит около 310k узлов (10-15MBytes); результаты представлены в таблице ниже, имеется 6 столбцов со временем в секундах;
1 - время создания узлов и записи значений
2 - SaveToFile();
3 = 1 + 2
4 - LoadFromFile();
5 - перемещение по узлам и чтение значений
6 = 4 + 5
Изображение 7795

MSXML/Xerces/ADOM - разные поставщики для TXMLDocument (DOMVendor)
JanXML не работает с unicode; Я исправил некоторые ошибки и сохранил XML, но загрузка вызывает AV (или переполнение стека, я не помню),
manual - означает вручную записывать XML с помощью TStringStream.

Я использовал Delphi2010, Win7x32, Q8200 CPU/2.3GHz, 4Gb ОЗУ.

update: вы можете скачать исходный код для этого теста (записать сериализацию в XML с помощью RTTI) здесь http://blog.karelia.pro/teran/files/2012/03/XMLTest.zip Все парсеры (Omni, Native, Ян) включены (теперь количество узлов в XML составляет около 270 тыс.), Извините, что в коде нет комментариев.

  • 4
    по крайней мере, некоторые ориентиры +1.
  • 0
    Хорошие цифры. Я просто добавлю, что NativeXML предпринял определенные шаги для добавления буферизованного потока, чтобы получить хорошую производительность. У меня был большой файл, который был очень медленным, а потом он был таким же быстрым, как маленький файл. Измерения выше показывают, что это хорошо.
Показать ещё 5 комментариев
20

Я знаю, что это старый вопрос, но людям может показаться интересным:

Я написал новую XML-библиотеку для Delphi (OXml): http://www.kluug.net/oxml.php

Он имеет прямую обработку XML (чтение + запись), парсер SAX, DOM и последовательный парсер DOM. Одним из преимуществ является то, что OXml поддерживает Delphi 6-Delphi XE5, FPC/Lazarus и С++ Builder на всех платформах (Win, MacOSX, Linux, iOS, Android).

OXml DOM основан на записи/указателях и обеспечивает лучшую производительность, чем любая другая библиотека XML:

Тест чтения возвращает время, в течение которого анализатор должен читать пользовательский XML DOM из файла (столбец "load" ) и записывать значения node в константу фиктивной функции (столбец "navigate" "). Файл закодирован в UTF-8 и размер составляет около 5,6 МБ.

Изображение 7796

Тест записи возвращает время, в течение которого парсеру необходимо создать DOM (столбец "create" ) и записать этот DOM в файл (столбец "save" ). Файл закодирован в UTF-8 и размер составляет около 11 МБ.

Изображение 7797

+ Плохая производительность записи OmniXML (оригинал) была результатом того, что OmniXML не использовал буферизацию для записи. Таким образом, запись в TFileStream была очень медленной. Я обновил OmniXML и добавил поддержку буферизации. Вы можете получить последний код OmniXML из SVN.

  • 1
    Здорово. Я проверю вашу новую библиотеку!
  • 3
    @oxo Отличная библиотека! Этот должен быть ответом!
Показать ещё 1 комментарий
6

Когда-нибудь я написал очень простой набор тестов XML. Он обслуживает MSXML (D7 MSXML3?), Omni XML (бит старый) и Jedi XML (последняя стабильная версия).

Результаты теста для файла 1,52 МБ:

Время загрузки файла XML MSXML: 240,20 [мс]

XML node выбор MSXML: 1,09 [s]

Время загрузки файла XML OmniXML: 2,25 [s]

XML node выбор OmniXML: 1,22 [s]

Время загрузки файла XML JclSimpleXML: 2,11 [s]

и нарушение доступа для JclSimpleXML node вариантов: |

К сожалению, у меня на самом деле не так много времени, чтобы исправить выше AV, но sorces содержатся ниже...

fmuMain.pas

program XmlEngines;

uses
  FastMM4,
  Forms,
  fmuMain in 'fmuMain.pas' {fmMain},
  uXmlEngines in 'uXmlEngines.pas',
  ifcXmlEngine in 'ifcXmlEngine.pas';

{$R *.res}

begin
  Application.Initialize;
  Application.Title := 'XML Engine Tester';
  Application.CreateForm(TfmMain, fmMain);
  Application.Run;
end.

fmuMain.pas

unit fmuMain;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, xmldom, XMLIntf, msxmldom, XMLDoc,
  //
  ifcXmlEngine, StdCtrls;

type
  TfmMain = class(TForm)
    mmoDebug: TMemo;
    dlgOpen: TOpenDialog;

    procedure FormCreate(Sender: TObject);
    procedure FormDestroy(Sender: TObject);

    procedure mmoDebugClick(Sender: TObject);

  private
    fXmlEngines: TInterfaceList;
    function Get_Engine(const aIx: Integer): IXmlEngine;

  protected
    property XmlEngine[const aIx: Integer]: IXmlEngine read Get_Engine;

    procedure Debug(const aInfo: string); // inline

  public
    procedure RegisterXmlEngine(const aEngine: IXmlEngine);

  end;

var
  fmMain: TfmMain;

implementation

{$R *.dfm}

uses
  uXmlEngines, TZTools;

{ TForm1 }

function TfmMain.Get_Engine(const aIx: Integer): IXmlEngine;
begin
  Result:= nil;
  Supports(fXmlEngines[aIx], IXmlEngine, Result)
end;

procedure TfmMain.RegisterXmlEngine(const aEngine: IXmlEngine);
var
  Ix: Integer;
begin
  if aEngine = nil then
    Exit; // WARRNING: program flow disorder

  for Ix:= 0 to Pred(fXmlEngines.Count) do
    if XmlEngine[Ix] = aEngine then
      Exit; // WARRNING: program flow disorder

  fXmlEngines.Add(aEngine)
end;

procedure TfmMain.FormCreate(Sender: TObject);
begin
  fXmlEngines:= TInterfaceList.Create();
  dlgOpen.InitialDir:= ExtractFileDir(ParamStr(0));
  RegisterXmlEngine(TMsxmlEngine.Create(Self));
  RegisterXmlEngine(TOmniXmlEngine.Create());
  RegisterXmlEngine(TJediXmlEngine.Create());
end;

procedure TfmMain.mmoDebugClick(Sender: TObject);

  procedure TestEngines(const aFilename: TFileName);

    procedure TestEngine(const aEngine: IXmlEngine);
    var
      PerfCheck: TPerfCheck;
      Ix: Integer;
    begin
      PerfCheck := TPerfCheck.Create();
      try

        PerfCheck.Init(True);
        PerfCheck.Start();
        aEngine.Load(aFilename);
        PerfCheck.Pause();
        Debug(Format(
          'XML file loading time %s: %s',
          [aEngine.Get_ID(), PerfCheck.TimeStr()]));

        if aEngine.Get_ValidNode() then
        begin
          PerfCheck.Start();
          for Ix:= 0 to 999999 do
            if aEngine.Get_ChildsCount() > 0 then
            begin

              aEngine.SelectChild(Ix mod aEngine.Get_ChildsCount());

            end
            else
              aEngine.SelectRootNode();

          PerfCheck.Pause();
          Debug(Format(
            'XML nodes selections %s: %s',
            [aEngine.Get_ID(), PerfCheck.TimeStr()]));
        end

      finally
        PerfCheck.Free();
      end
    end;

  var
    Ix: Integer;
  begin
    Debug(aFilename);
    for Ix:= 0 to Pred(fXmlEngines.Count) do
      TestEngine(XmlEngine[Ix])
  end;

var
  CursorBckp: TCursor;
begin
  if dlgOpen.Execute() then
  begin

    CursorBckp:= Cursor;
    Self.Cursor:= crHourGlass;
    mmoDebug.Cursor:= crHourGlass;
    try
      TestEngines(dlgOpen.FileName)
    finally
      Self.Cursor:= CursorBckp;
      mmoDebug.Cursor:= CursorBckp;
    end

  end
end;

procedure TfmMain.Debug(const aInfo: string);
begin
  mmoDebug.Lines.Add(aInfo)
end;

procedure TfmMain.FormDestroy(Sender: TObject);
begin
  fXmlEngines.Free()
end;

end.

ifcXmlEngine.pas

unit ifcXmlEngine;

interface

uses
  SysUtils;

type
  TFileName = SysUtils.TFileName;

  IXmlEngine = interface
    ['{AF77333B-9873-4FDE-A3B1-260C7A4D3357}']
    procedure Load(const aFilename: TFileName);
    procedure SelectRootNode();
    procedure SelectChild(const aIndex: Integer);
    procedure SelectParent();
    //
    function Get_ID(): string;
    function Get_ValidNode(): Boolean;
    function Get_ChildsCount(): Integer;
    function Get_HaveParent(): Boolean;
    //function Get_NodeName(): Boolean;
  end;

implementation

end.

uXmlEngines.pas

unit uXmlEngines;

interface

uses
  Classes,
  //
  XMLDoc, XMLIntf, OmniXml, JclSimpleXml,
  //
  ifcXmlEngine;

type
  TMsxmlEngine = class(TInterfacedObject, IXmlEngine)
  private
    fXmlDoc: XMLDoc.TXMLDocument;
    fNode: XMLIntf.IXMLNode;

  protected

  public
    constructor Create(const aOwner: TComponent);
    destructor Destroy; override;

    procedure Load(const aFilename: TFileName);
    procedure SelectRootNode();
    procedure SelectChild(const aIndex: Integer);
    procedure SelectParent();
    //
    function Get_ID(): string;
    function Get_ValidNode(): Boolean;
    function Get_ChildsCount(): Integer;
    function Get_HaveParent(): Boolean;
    //function Get_NodeName(): Boolean;

  end;

  TOmniXmlEngine = class(TInterfacedObject, IXmlEngine)
  private
    fXmlDoc: OmniXml.IXmlDocument;
    fNode: OmniXml.IXMLNode;

  protected

  public
    constructor Create;
    destructor Destroy; override;

    procedure Load(const aFilename: TFileName);
    procedure SelectRootNode();
    procedure SelectChild(const aIndex: Integer);
    procedure SelectParent();
    //
    function Get_ID(): string;
    function Get_ValidNode(): Boolean;
    function Get_ChildsCount(): Integer;
    function Get_HaveParent(): Boolean;
    //function Get_NodeName(): Boolean;

  end;

  TJediXmlEngine = class(TInterfacedObject, IXmlEngine)
  private
    fXmlDoc: TJclSimpleXML;
    fNode: TJclSimpleXMLElem;

  protected

  public
    constructor Create();
    destructor Destroy(); override;

    procedure Load(const aFilename: TFileName);
    procedure SelectRootNode();
    procedure SelectChild(const aIndex: Integer);
    procedure SelectParent();
    //
    function Get_ID(): string;
    function Get_ValidNode(): Boolean;
    function Get_ChildsCount(): Integer;
    function Get_HaveParent(): Boolean;
    //function Get_NodeName(): Boolean;

  end;

implementation

uses
  SysUtils;

{ TMsxmlEngine }

constructor TMsxmlEngine.Create(const aOwner: TComponent);
begin
  if aOwner = nil then
    raise Exception.Create('TMsxmlEngine.Create() -> invalid owner');

  inherited Create();
  fXmlDoc:= XmlDoc.TXmlDocument.Create(aOwner);
  fXmlDoc.ParseOptions:= [poPreserveWhiteSpace]
end;

destructor TMsxmlEngine.Destroy;
begin
  fXmlDoc.Free();
  inherited Destroy()
end;

function TMsxmlEngine.Get_ChildsCount: Integer;
begin
  Result:= fNode.ChildNodes.Count
end;

function TMsxmlEngine.Get_HaveParent: Boolean;
begin
  Result:= fNode.ParentNode <> nil
end;

function TMsxmlEngine.Get_ID: string;
begin
  Result:= 'MSXML'
end;

//function TMsxmlEngine.Get_NodeName: Boolean;
//begin
//  Result:= fNode.Text
//end;

function TMsxmlEngine.Get_ValidNode: Boolean;
begin
  Result:= fNode <> nil
end;

procedure TMsxmlEngine.Load(const aFilename: TFileName);
begin
  fXmlDoc.LoadFromFile(aFilename);
  SelectRootNode()
end;

procedure TMsxmlEngine.SelectChild(const aIndex: Integer);
begin
  fNode:= fNode.ChildNodes.Get(aIndex)
end;

procedure TMsxmlEngine.SelectParent;
begin
  fNode:= fNode.ParentNode
end;

procedure TMsxmlEngine.SelectRootNode;
begin
  fNode:= fXmlDoc.DocumentElement
end;

{ TOmniXmlEngine }

constructor TOmniXmlEngine.Create;
begin
  inherited Create();
  fXmlDoc:= OmniXml.TXMLDocument.Create();
  fXmlDoc.PreserveWhiteSpace:= true
end;

destructor TOmniXmlEngine.Destroy;
begin
  fXmlDoc:= nil;
  inherited Destroy()
end;

function TOmniXmlEngine.Get_ChildsCount: Integer;
begin
  Result:= fNode.ChildNodes.Length
end;

function TOmniXmlEngine.Get_HaveParent: Boolean;
begin
  Result:= fNode.ParentNode <> nil
end;

function TOmniXmlEngine.Get_ID: string;
begin
  Result:= 'OmniXML'
end;

//function TOmniXmlEngine.Get_NodeName: Boolean;
//begin
//  Result:= fNode.NodeName
//end;

function TOmniXmlEngine.Get_ValidNode: Boolean;
begin
  Result:= fNode <> nil
end;

procedure TOmniXmlEngine.Load(const aFilename: TFileName);
begin
  fXmlDoc.Load(aFilename);
  SelectRootNode()
end;

procedure TOmniXmlEngine.SelectChild(const aIndex: Integer);
begin
  fNode:= fNode.ChildNodes.Item[aIndex]
end;

procedure TOmniXmlEngine.SelectParent;
begin
  fNode:= fNode.ParentNode
end;

procedure TOmniXmlEngine.SelectRootNode;
begin
  fNode:= fXmlDoc.DocumentElement
end;

{ TJediXmlEngine }

constructor TJediXmlEngine.Create;
begin
  inherited Create();
  fXmlDoc:= TJclSimpleXML.Create();
end;

destructor TJediXmlEngine.Destroy;
begin
  fXmlDoc.Free();
  inherited Destroy()
end;

function TJediXmlEngine.Get_ChildsCount: Integer;
begin
  Result:= fNode.ChildsCount
end;

function TJediXmlEngine.Get_HaveParent: Boolean;
begin
  Result:= fNode.Parent <> nil
end;

function TJediXmlEngine.Get_ID: string;
begin
  Result:= 'JclSimpleXML';
end;

//function TJediXmlEngine.Get_NodeName: Boolean;
//begin
//  Result:= fNode.Name
//end;

function TJediXmlEngine.Get_ValidNode: Boolean;
begin
  Result:= fNode <> nil
end;

procedure TJediXmlEngine.Load(const aFilename: TFileName);
begin
  fXmlDoc.LoadFromFile(aFilename);
  SelectRootNode()
end;

procedure TJediXmlEngine.SelectChild(const aIndex: Integer);
begin
  fNode:= fNode.Items[aIndex]
end;

procedure TJediXmlEngine.SelectParent;
begin
  fNode:= fNode.Parent
end;

procedure TJediXmlEngine.SelectRootNode;
begin
  fNode:= fXmlDoc.Root
end;

end.
3

Попробуйте himXML himitsu.

Он выпущен под лицензией MPL v1.1, GPL v3.0 или LGPL v3.0.

Вам нужно будет зарегистрироваться на Delphi-Praxis (немецкий) отличный сайт Delphi, чтобы иметь возможность скачивать:

Он имеет очень впечатляющую производительность, и в дистрибутив включены демонстрации, демонстрирующие это. Я успешно использовал его в Delphi 2007, Delphi 2010 и Delphi XE.

  • 3
    Можете ли вы обновить сравнительный тест по крайней мере с MSXML?

Ещё вопросы

Сообщество Overcoder
Наверх
Меню