edition - ¿Cuál es el analizador XML más rápido disponible para Delphi?
delphi versiones (5)
Tenemos cadenas de XML razonablemente grandes que actualmente analizamos utilizando MSXML2
¡Acabo de intentar usar MSXML6 esperando una mejora de velocidad y no tengo nada!
Actualmente creamos una gran cantidad de documentos DOM y creo que puede haber un poco de sobrecarga en la interacción constante con el dll MSXML2 / 6
¿Alguien sabe de un componente XML mejor / más rápido para Delphi?
Si alguien puede sugerir una alternativa, y es más rápida, buscaríamos integrarla, pero eso sería mucho trabajo, por lo que esperamos que la estructura no sea muy diferente a la utilizada por MSXML.
Estamos usando Delphi 2010
Pablo
Algún día escribí un conjunto de pruebas XML muy simple. Sirve MSXML (D7 MSXML3?), Omni XML (poco antiguo) y Jedi XML (último estable).
Resultados de prueba para 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 de archivos XML OmniXML: 2,25 [s]
Selecciones de nodos XML OmniXML: 1,22 [s]
Tiempo de carga de archivos XML JclSimpleXML: 2,11 [s]
y violación de acceso para selecciones de nodo JclSimpleXML: |
Desafortunadamente, en realidad no tengo mucho tiempo para corregir el AV anterior, pero las hechiceras se encuentran 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.
Recientemente tuve un problema similar en el que el uso del analizador MSXML DOM resultó ser demasiado lento para la tarea dada. Tuve que analizar documentos bastante grandes> 1 MB y el consumo de memoria del analizador DOM era prohibitivo. Mi solución fue no utilizar un analizador DOM en absoluto, sino ir con el analizador MSXML SAX impulsado por eventos. Esto resultó ser mucho, mucho más rápido. Lamentablemente, 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 MSXML SAX en Delphi : SAX, Delphi y Ex Em El
Sé que es una vieja pregunta, pero la gente puede 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. Una de las ventajas es que OXml admite Delphi 6-Delphi XE5, FPC / Lazarus y C ++ Builder en todas las plataformas (Win, MacOSX, Linux, iOS, Android).
OXml DOM está basado en registros / punteros 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 desde un archivo (columna "cargar") y para escribir valores de nodo en una función ficticia constante (columna "navegar"). El archivo está codificado en UTF-8 y su tamaño es de aproximadamente 5,6 MB.
La prueba de escritura devuelve el tiempo que el analizador necesita para crear un DOM (columna "crear") y escribe este DOM en un archivo (columna "guardar"). El archivo está codificado en UTF-8 y su tamaño es de aproximadamente 11 MB.
+ El pobre rendimiento de escritura de OmniXML (original) fue el resultado del hecho de que OmniXML no usó el almacenamiento en búfer para escribir. Por lo tanto, escribir en un TFileStream fue muy lento. Actualicé OmniXML y agregué soporte de almacenamiento en búfer. Puede obtener el último código OmniXML del SVN.
Se lanza bajo la licencia MPL v1.1 , GPL v3.0 o LGPL v3.0 .
Tendrá que registrarse en el excelente sitio Delphi-Praxis (alemán) de Delphi para poder descargar:
Tiene un rendimiento muy impresionante y la distribución incluye demostraciones que demuestran eso. Lo utilicé con éxito en Delphi 2007, Delphi 2010 y Delphi XE.
Hace algún tiempo tuve que serializar el record
al 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 por los campos de registro y almacenar valores en XML. He probado algunos XML Parsers. No necesitaba el modelo DOM para crear xml, pero lo necesitaba para cargarlo.
XML contenía aproximadamente 310k nodos (10-15MBytes); los 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 los nodos y leer valores
6 = 4 + 5
MSXML/Xerces/ADOM
- son diferentes proveedores para TXMLDocument
( DOMVendor
)
JanXML
no funciona con Unicode; Solucioné algunos errores y guardé XML, pero cargar causa AV (o desbordamiento de pila, no recuerdo);
manual
: significa escribir manualmente XML utilizando TStringStream
.
Usé Delphi2010, Win7x32, CPU Q8200 / 2.3GHz, 4Gb de RAM.
actualización: Puede descargar el código fuente para esta prueba (registro de serialización a XML usando RTTI) aquí http://blog.karelia.pro/teran/files/2012/03/XMLTest.zip Todos los analizadores (Omni, Native, Jan) son incluido (ahora los nodos cuentan en XML es de aproximadamente 270k), lo siento, no hay comentarios en el código.