¿Cuál es el analizador XML más rápido disponible para Delphi?


Tenemos cadenas XML razonablemente grandes que actualmente analizamos usando MSXML2

Acabo de intentar usar MSXML6 esperando una mejora de velocidad y no tengo nada!

Actualmente creamos muchos documentos DOM y supongo que puede haber cierta sobrecarga en la interacción constante con la dll de MSXML2/6

¿Alguien conoce un componente XML mejor/más rápido para Delphi?

Si alguien puede sugerir una alternativa, y es más rápido, buscaríamos integrarla, pero eso sería mucho trabajo, así que esperemos que la estructura no sea demasiado diferente a la utilizada por MSXML

Estamos usando Delphi 2010

Paul

Author: Paul, 2012-02-28

5 answers

Recientemente tuve un problema similar en el que el uso del analizador DOM de MSXML resultó ser demasiado lento para la tarea dada. Tuve que analizar documentos bastante grandes > 1MB y el consumo de memoria del analizador DOM era prohibitivo. Mi solución fue no usar un analizador DOM en absoluto, sino ir con el analizador SAX MSXML impulsado por eventos. Esto resultó ser mucho, mucho más rápido. Desafortunadamente el modelo de programación es totalmente diferente, pero dependiendo de la tarea, podría valer la pena. Craig Murphy ha publicado un excelente artículo sobre cómo usar el analizador SAX de MSXML en delphi: SAX, Delphi y Ex Em El

 11
Author: Lars Frische,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-02-29 08:04:27

Hace algún tiempo tuve que serializar record a formato XML; por ejemplo:

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

A

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

Utilicé RTTI para navegar recursivamente a través de campos de registro y almacenar valores en XML. He probado algunos analizadores XML. No necesitaba el modelo DOM para crear xml, pero lo necesitaba para cargarlo de nuevo.

XML contiene alrededor de 310k nodos (10-15MBytes); resultados presentados en la tabla a continuación, hay 6 columnas con tiempo en segundos;
1-tiempo para crear nodos y escribir valores
2-SaveToFile ();
3 = 1 + 2
4-LoadFromFile ();
5-navegar a través de nodos y leer valores
6 = 4 + 5
introduzca la descripción de la imagen aquí

MSXML/Xerces/ADOM - son diferentes proveedores para TXMLDocument (DOMVendor)
JanXML no funciona con unicode; Corregí algunos errores y guardé XML, pero la carga causa AV (o desbordamiento de pila, no recuerdo);
manual - significa escribir XML manualmente usando TStringStream.

Usé Delphi2010, Win7x32, CPU Q8200/2.3 GHz, 4 Gb de RAM.

Actualización: Usted puede descargue el código fuente para esta prueba (registre la serialización a XML usando RTTI) aquí http://blog.karelia.pro/teran/files/2012/03/XMLTest.zip Todos los analizadores (Omni, Native, Jan) están incluidos (ahora el recuento de nodos en XML es de aproximadamente 270k), lo sentimos, no hay comentarios en el código.

 34
Author: teran,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-03-01 07:50:23

Sé que es una vieja pregunta, pero la gente podría encontrarla interesante:

Escribí una nueva biblioteca XML para Delphi (OXml): http://www.kluug.net/oxml.php

Cuenta con manejo directo de XML (lectura+escritura), analizador SAX, DOM y un analizador DOM secuencial. Uno de los beneficios es que OXml soporta Delphi 6-Delphi XE5, FPC/Lazarus y C++Builder en todas las plataformas (Win, MacOSX, Linux, iOS, Android).

OXml DOM se basa en registros / puntero y ofrece un mejor rendimiento que cualquier otra biblioteca XML:

La prueba de lectura devuelve el tiempo que el analizador necesita para leer un DOM XML personalizado de un archivo (columna "load") y para escribir valores de nodo en una función simulada constante (columna "navigate"). El archivo está codificado en UTF-8 y su tamaño es de aproximadamente 5,6 MB.

Comparación de análisis XML

La prueba de escritura devuelve el tiempo que el analizador necesita para crear un DOM (columna "create") y escribir este DOM en un archivo (columna "save"). El archivo está codificado en UTF-8 y su tamaño es unos 11 MB.

Comparación de escritura XML

+ El pobre rendimiento de escritura de OmniXML (original) fue el resultado del hecho de que OmniXML no usaba buffering para escribir. Así escribir a TFileStream era muy lento. Actualizé OmniXML y agregué soporte de almacenamiento en búfer. Puede obtener el último código OmniXML del SVN.

 22
Author: oxo,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-02-11 20:58:04

Algún día he escrito un conjunto de pruebas XML muy simple. Sirve MSXML (D7 MSXML3?), Omni XML (bit old) y Jedi XML (latest stable).

Resultados de la prueba para el archivo de 1,52 MB:

Tiempo de carga del archivo XML MSXML: 240,20 [ms]

Selecciones de nodos XML MSXML: 1,09 [s]

Tiempo de carga del archivo XML OmniXML: 2,25 [s]

Selecciones de nodos XML OmniXML: 1,22 [s]

Tiempo de carga del archivo XML JclSimpleXML: 2,11 [s]

Y violación de acceso para selecciones de nodos JclSimpleXML: /

Desafortunadamente en realidad no tengo mucho tiempo para corregir por encima de AV, pero sorces están contenidos a continuación...

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.
 6
Author: g2mk,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-02-29 06:30:14

Darle una oportunidad a himXML por himitsu.

Está publicado bajo MPL v1.1 , la GPL v3.0 o LGPL v3.0 licencia.

Tendrá que registrarse en el Delphi-Praxis (alemán) excelente sitio Delphi para poder descargar:

Tiene un rendimiento muy impresionante y la distribución incluye demos que lo demuestran. Lo he usado con éxito en Delphi 2007, Delphi 2010 y Delphi XE.

 3
Author: menjaraz,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-02-29 05:08:24