Delphi中最快的XML解析器是什么?

Pau*_*aul 31 delphi delphi-2010

我们有相当大的XML字符串,我们目前使用MSXML2进行解析

我刚刚尝试使用MSXML6希望提高速度,什么都没有!

我们目前创建了很多DOM文档,我想在不断与MSXML2/6 dll交互时可能会有一些开销

有没有人知道Delphi的更好/更快的XML组件?

如果有人可以提出替代方案,并且它更快,我们会考虑整合它,但这将是很多工作,所以希望结构不会与MSXML使用的结构太不一样

我们正在使用Delphi 2010

保罗

ter*_*ran 34

前段时间我不得不序列化为recordXML格式; 对于前:

 TTest = record
    a : integer;
    b : real; 
 end;
Run Code Online (Sandbox Code Playgroud)

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

我使用RTTI递归浏览记录字段并将值存储到XML.我尝试过很少的XML Parsers.我不需要DOM模型来创建xml,但是需要它来加载它.

XML包含大约310k 节点(10-15MBytes); 结果如下表所示,有6列,时间以秒为单位;
1 - 创建节点和写入值的时间
2 - SaveToFile();
3 = 1 + 2
4 - LoadFromFile();
5 - 浏览节点并读取值
6 = 4 + 5
在此输入图像描述

MSXML/Xerces/ADOM- TXMLDocument(DOMVendor)的差异供应商
JanXML不适用于unicode; 我修复了一些错误,并保存了XML,但加载导致AV(或堆栈溢出,我不记得了);
manual- 表示使用手动编写XML TStringStream.

我使用的是Delphi2010,Win7x32,Q8200 CPU/2.3GHz,4Gb的RAM.

更新:您可以在此处下载此测试的源代码(使用RTTI将序列化记录到XML)http://blog.karelia.pro/teran/files/2012/03/XMLTest.zip所有解析器(Omni,Native,Jan)都是包含(现在XML中的节点数约为270k),遗憾的是代码中没有注释.


oxo*_*oxo 23

我知道这是一个老问题,但人们可能会发现它很有趣:

我为Delphi(OXml)编写了一个新的XML库:http://www.kluug.ne​​t/oxml.php

它具有直接XML处理(读取+写入),SAX解析器,DOM和顺序DOM解析器.其中一个好处是OXml在所有平台(Win,MacOSX,Linux,iOS,Android)上都支持Delphi 6-Delphi XE5,FPC/Lazarus和C++ Builder.

OXml DOM基于记录/指针,提供比任何其他XML库更好的性能:

读取测试返回解析器需要从文件(列"load")读取自定义XML DOM并将节点值写入常量虚函数(列"导航")的时间.该文件以UTF-8编码,其大小约为5.6 MB.

XML解析比较

写测试返回解析器创建DOM(列"create")并将此DOM写入文件(列"save")所需的时间.该文件以UTF-8编码,其大小约为11 MB.

XML写比较

+糟糕的OmniXML(原始)写入性能是OmniXML不使用缓冲进行写入的结果.因此写入TFileStream非常慢.我更新了OmniXML并添加了缓冲支持.您可以从SVN获取最新的OmniXML代码.

  • @oxo很棒的图书馆!这应该是答案! (3认同)
  • @oxo 抱歉,OXml 库不是免费的。 (2认同)

小智 11

最近我遇到了类似的问题,使用MSXML DOM解析器对于给定的任务来说太慢了.我不得不解析大于1MB的大型文档,并且DOM解析器的内存消耗过高.我的解决方案是根本不使用DOM解析器,而是使用事件驱动的MSXML SAX解析器.事实证明,这要快得多.不幸的是,编程模型完全不同,但依赖于任务,它可能是值得的.Craig Murphy发表了一篇关于如何在delphi中使用MSXML SAX解析器的优秀文章: SAX,Delphi和Ex Em El


g2m*_*2mk 6

总有一天我写了非常简单的XML测试套件.它提供MSXML(D7 MSXML3?),Omni XML(旧位)和Jedi XML(最新稳定版).

1,52 MB文件的测试结果:

XML文件加载时间MSXML:240,20 [ms]

XML节点选择MSXML:1,09 [s]

XML文件加载时间OmniXML:2,25 [s]

XML节点选择OmniXML:1,22 [s]

XML文件加载时间JclSimpleXML:2,11 [s]

和JclSimpleXML节点选择的访问冲突:|

不幸的是,我实际上没有太多时间纠正上面的AV,但是下面包含了...

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.
Run Code Online (Sandbox Code Playgroud)

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.
Run Code Online (Sandbox Code Playgroud)

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.
Run Code Online (Sandbox Code Playgroud)

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.
Run Code Online (Sandbox Code Playgroud)