2012-06-05 26 views
6

que tienen un XML con el siguiente formato:Lectura valores distintos de XML usando XPath

<Accounts> 
    <Account Number="1" DebitAmount="1000" Amount="2827561.95" /> 
    <Account Number="225" DebitAmount="2000" Amount="12312.00" /> 
    <Account Number="236" DebitAmount="London" Amount="457656.00" /> 
    <Account Number="225" DebitAmount="London" Amount="23462.40" /> 
    <Account Number="236" DebitAmount="Bangalore" Amount="2345345.00" /> 
</Accounts> 

¿Cómo retreive los números de cuenta únicos utilizando Xpath? es decir, que quieren obtener los valores de 1, 225 y 236.

Esto es lo que hice: (estoy usando Delphi 2007 ...)

Const XmlStr = 
' <Accounts> 
    <Account Number="1" DebitAmount="1000" Amount="2827561.95" /> 
    <Account Number="225" DebitAmount="2000" Amount="12312.00" /> 
    <Account Number="236" DebitAmount="London" Amount="457656.00" /> 
    <Account Number="225" DebitAmount="London" Amount="23462.40" /> 
    <Account Number="236" DebitAmount="Bangalore" Amount="2345345.00" /> 
</Accounts>'; 

function GetAccountNumbers:TList; 
Var 
    XMLDOMDocument : IXMLDOMDocument; 
    accounts : IXMLDOMNodeList; 
    accountdetail :IXMLDOMNode; 
    i:Integer 
    list :TList 
begin 
    Result:=TList.Create; 
    XMLDOMDocument:=CoDOMDocument.Create; 
    XMLDOMDocument.loadXML(XmlStr); 
    accounts:= XMLDOMDocument.SelectNodes(''./Accounts 
    /Account[not(@Number=preceding-sibling/ Account /@Number)]'); 
    for i := 0 to accountdetails.length - 1 do begin 
    accountdetail := accountdetails.item[i]; 
    //omitting the "<>nil" checks... 
    list.Add(accountdetail.attributes.getNamedItem('Number').Nodevalue; 
    end; 
end; 

Pero esto no devolvió nodos (AccountDetails. longitud = 0). Por favor, hágame saber lo que me falta aquí.

Gracias,

Pradeep

+4

¿Es esa pieza parte de su código real? Al mirarlo, ni siquiera compilará. – TLama

+0

He copiado eso; pero no corrigió los apóstrofes. Solo quería dar una idea de lo que hice ... – Pradeep

+0

Para responder por qué está recibiendo 'IXMLDOMNodeList' la lista de longitud cero es simple. Es porque ha intentado solucionar el error sobre los caracteres '::' inválidos en 'previous-sibling ::' reemplazándolo con '/', pero esto significa que está comparando el valor con el nodo 'previous-sibling/.. 'qué está mal. Necesita usar 'previous-sibling' como parámetro (usando' :: ') devolviendo el nodo anterior, no como un valor de nodo constante. Para usar el eje XPath, debe usar la versión más nueva de MSXML, ya que la anterior no es compatible. – TLama

Respuesta

5

Parece que la versión del MSXML de Delphi 2007 no admite el eje XPath. Así que si usted decide utilizar el siguiente código, ya sea de importación o Microsoft XML, v3.0 la biblioteca Microsoft XML, v6.0 tipo primero y luego intentar esto:

unit Unit1; 

interface 

uses 
    Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, 
    Dialogs, StdCtrls, MSXML2_TLB; 

type 
    TForm1 = class(TForm) 
    Button1: TButton; 
    procedure Button1Click(Sender: TObject); 
    private 
    { Private declarations } 
    public 
    { Public declarations } 
    end; 

var 
    Form1: TForm1; 

implementation 

{$R *.dfm} 

const 
    XMLString = 
    '<Accounts>' + 
    '<Account Number="1" DebitAmount="1000" Amount="2827561.95"/>' + 
    '<Account Number="225" DebitAmount="2000" Amount="12312.00"/>' + 
    '<Account Number="236" DebitAmount="London" Amount="457656.00"/>' + 
    '<Account Number="225" DebitAmount="London" Amount="23462.40"/>' + 
    '<Account Number="236" DebitAmount="Bangalore" Amount="2345345.00"/>' + 
    '</Accounts>'; 

type 
    TIntegerArray = array of Integer; 

function GetAccountNumbers(const AXMLString: string): TIntegerArray; 
var 
    I: Integer; 
    XMLDOMNodeList: IXMLDOMNodeList; 
    XMLDOMDocument: IXMLDOMDocument3; 
begin 
    XMLDOMDocument := CoDOMDocument60.Create; 
    if Assigned(XMLDOMDocument) and XMLDOMDocument.loadXML(AXMLString) then 
    begin 
    XMLDOMNodeList := XMLDOMDocument.selectNodes('/Accounts/Account[not(@Number=preceding-sibling::Account/@Number)]/@Number'); 
    SetLength(Result, XMLDOMNodeList.length); 
    for I := 0 to XMLDOMNodeList.length - 1 do 
     Result[I] := XMLDOMNodeList.item[I].nodeValue; 
    end; 
end; 

procedure TForm1.Button1Click(Sender: TObject); 
var 
    S: string; 
    I: Integer; 
    IntegerArray: TIntegerArray; 
begin 
    S := 'Account numbers: '; 
    IntegerArray := GetAccountNumbers(XMLString); 
    for I := 0 to Length(IntegerArray) - 1 do 
    S := S + IntToStr(IntegerArray[I]) + ', '; 
    Delete(S, Length(S) - 1, 2); 
    ShowMessage(S); 
end; 

end. 
+0

Instalé el Microsoft xml 6.0, pero sigo recibiendo la excepción del depurador. (Ahora estoy interesado en conocer el método) – Zeina

+0

@Zeina, no me dijiste tanto, ¿qué excepción y cuándo? ¿Quieres decir cuándo los nodos están siendo seleccionados por el XPath? Probé el código de esta publicación en Delphi 2007 para Windows 7 y Windows XP y funciona bien para mí. – TLama

+0

este es el mensaje: Clase de excepción EOleException con el mensaje 'Token esperado') 'found': ' ./Accounts/Account[not(@Number=preceding-sibling-->:<--:Account/@Number)] /@Número'. ¿El Misrosoft XML no está instalado correctamente? – Zeina

0

probar este código:

XMLDocument1: TXMLDocument; 

procedure TForm1.Button2Click(Sender: TObject); 
var 
    BodyNode:IXMLNode; 
    I: Integer; 
    sl:TStringList; 
begin 
    sl:=TStringList.Create; 
    XMLDocument1.Active:=False; 
    XMLDocument1.FileName:='C:\Zeina\Test.xml'; 
    XMLDocument1.Active:=True; 

    BodyNode:=XMLDocument1.DocumentElement; 
    for I:=0 to BodyNode.ChildNodes.Count-1 do 
    begin 
    if sl.IndexOf(BodyNode.ChildNodes[i].Attributes['Number'])=-1 then 
     sl.Add(BodyNode.ChildNodes[i].Attributes['Number']); 
    end; 
    Memo1.Lines:=sl; 
end; 

este es el resultado:

+2

Aunque funciona, pero no es la forma de XPath, lo que se preguntó aquí. – TLama

-1

Ziena, puedo cambiar su código:

XMLDocument1: TXMLDocument; 

procedure TForm1.Button2Click(Sender: TObject); 
var 
    BodyNode:IXMLNode; 
    I: Integer; 
    sl:TStringList; 
begin 
    sl:=TStringList.Create; 
    XMLDocument1.Active:=False; 
    XMLDocument1.FileName:='C:\Zeina\Test.xml'; 
    XMLDocument1.Active:=True; 

    BodyNode:=XMLDocument1.DocumentElement; 
    sl.Duplicates := dupIgnore; 
    for I:=0 to BodyNode.ChildNodes.Count-1 do 
    sl.Add(
     BodyNode.ChildNodes[i].Attributes['Number']); // ignore duplicated lines 
    Memo1.Lines.AddStrings(sl); // for avoid memory leak 
end; 
+2

y el XPath? – TLama

1

no entienda exactamente lo que usted quiere lograr. ¿Tal vez esto?

'/Accounts/Account[not(@Number=preceding-sibling::node()/@Number)]/@Number' 
+0

Esto fallará con la versión anterior de la biblioteca de tipo MSXML enviada con Delphi 2007 con el error 'Clase de excepción EOleException con el mensaje 'Token esperado') 'found': './Cuentas/Cuenta [no (@ Number = previous-sibling ->: <-: node()/@ Number)] '. ' – TLama

+0

@TLama Que se debe usar la biblioteca de tipo más nueva, como sugirió en su respuesta . No es claro para mí que la pregunta sea sobre usar xpath y hachas en Delphi 2007, o sobre la expresión xpath para usar? o tal vez ambos? – balazs

+0

Estaba buscando una solución usando la expresión xpath que funciona en mi versión de Delphi ... – Pradeep

1

Esto funciona en mi XPath example in Delphi XE2 y una Delphi 2007 update of the XPath example da este resultado:

nodeName[0]:Number 
nodeValue[0]:1 
nodeName[1]:Number 
nodeValue[1]:225 
nodeName[2]:Number 
nodeValue[2]:236 

Las ideas en el ejemplo deben conseguir que ir con Delphi y 2007 utilizando el MSXML 6 DOM, o el OpenXML DOM (Delphi XE2 es compatible con MSXML 6 DOM o ADOM XML v4 DOM).

Tenga en cuenta que el comportamiento de MSXML 6 depende en gran medida de la versión real que haya instalado, que depende de su sistema operativo Windows (de ahí this answer).

Lo que probablemente significa que no tiene instalada la última versión de MS XML 6, o no ha importado las bibliotecas de tipos correctas (la unidad msxml Delphi 2007 no contiene IXMLDOMDocument2 que necesita para el soporte de XPath).

El 27 de julio de 2012, me tomé un tiempo para adaptar el ejemplo a Delphi 2007 y probarlo.
No estoy completamente seguro de que esté completamente libre de fugas de memoria (pirateé una clase TDictionary para poner interfaces), pero los resultados son los mismos que en el ejemplo de Delphi XE2 y, a primera vista, se ve bastante bien.

En la aplicación de demostración XPathTester, cargue el Ejemplo 3, luego Ejecute el XPath (que cargará su ejemplo y ejecutará XPath).

Tenga en cuenta que la mayoría de las otras cosas en el bo library requieren al menos Delphi 2009, pero esta parte funciona.
En las próximas semanas lo probaré como lo requiero en un proyecto diferente que todavía está en Delphi 2007.

procedure TMainForm.LoadXmlExample3ButtonClick(Sender: TObject); 
begin 
    LoadXmlExample([ // unique account numbers 
    '/Accounts/Account[not(@Number=preceding-sibling::Account/@Number)]/@Number' 
    ], 
    [ 
    '<?xml version="1.0"?>', 
    '<Accounts>', 
    ' <Account Number="1" DebitAmount="1000" Amount="2827561.95" />', 
    ' <Account Number="225" DebitAmount="2000" Amount="12312.00" />', 
    ' <Account Number="236" DebitAmount="London" Amount="457656.00" />', 
    ' <Account Number="225" DebitAmount="London" Amount="23462.40" />', 
    ' <Account Number="236" DebitAmount="Bangalore" Amount="2345345.00" />', 
    '</Accounts>' 
    ]); 
end; 
+0

Gracias por la solución, pero esto no funcionará en mi versión de Delphi .. – Pradeep

+0

Actualmente mi respuesta es para demostrar que la consulta XPath trabaja en XML usando MSXML 6. Entonces sí: también debería ser posible hacerlo en Delphi 2007. Si/cuando encuentro tiempo para traducir el proyecto de ejemplo a Delphi 2007, se lo haré saber mediante una edición de esta respuesta. –

+0

@ user466744 Finalmente tuve la oportunidad de trabajar en portar la demo XPath do Delphi 2007. Por favor, vea si puede ejecutar el código que está almacenado aquí: http://bo.codeplex.com/SourceControl/changeset/80654 –

Cuestiones relacionadas