Reading various values ​​from XML using XPath

I have XML in the following format:

<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> 

How can I recover unique account numbers using Xpath? those. I want to get the values ​​1, 225 and 236.

This is what I did: (I am using 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; 

But this does not return nodes (accountdetails.length = 0). Please let me know what I'm missing here.

Thanks,

Pradeep

+6
source share
5 answers

Delphi 2007 MSXML version does not seem to support the XPath axis. Therefore, if you decide to use the following code, first import the Microsoft XML, v3.0 or Microsoft XML, v6.0 type library Microsoft XML, v6.0 , and then try the following:

 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. 
+5
source

I don’t understand what exactly you want to achieve. Maybe this?

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

This works in my XPath example in Delphi XE2 and the Delphi 2007 Update in the XPath example gives this result:

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

The ideas in this example should help you work with Delphi and 2007 using either the MSXML 6 DOM or the OpenXML DOM (Delphi XE2 supports MSXML 6 DOM or ADOM XML v4 DOM).

Please note that the behavior of MSXML 6 is highly dependent on the version you have installed, which depends on your Windows OS (hence this answer ).

Which probably means that you have not installed the latest MS XML 6 or the correct libraries have not been imported from it (the msxml block of Delphi 2007 does not contain IXMLDOMDocument2 , which you need to support XPath).

On July 27, 2012, I made some time to retro-fit the Delphi 2007 example and test it.
I’m not quite sure that this is a complete memory leak (I hacked into the TDictionary class for inputting interfaces), but the results are the same as the Delphi XE2 example, and at first glance it looks quite normal.

In the XPathTester demo application XPathTester download Example 3, then run XPath (which will load your example and run XPath).

Note that most of the other things in the bo library require at least Delphi 2009, but this part works.
In the following weeks I will test it, as I require it in another project that is still in 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; 
+1
source

try this code:

 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; 

this is the result:

0
source

Ziena, I am changing my code:

  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; 
-1
source

Source: https://habr.com/ru/post/917418/


All Articles