我有以下格式的XML:
<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>
如何使用Xpath检索唯一帐号?即,我想得到值1,225和236。
这就是我所做的:(我正在使用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;
但是这不返回任何节点(accountdetails.length = 0)。请告诉我这里缺少的东西。
谢谢,
与Pradeep
答案 0 :(得分:5)
似乎Delphi 2007的MSXML版本不支持XPath轴。因此,如果您决定使用以下代码,请先导入Microsoft XML, v3.0
或Microsoft XML, v6.0
类型库,然后尝试以下操作:
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.
答案 1 :(得分:1)
不清楚你想要达到什么目标。也许这个?
'/Accounts/Account[not(@Number=preceding-sibling::node()/@Number)]/@Number'
答案 2 :(得分:1)
这适用于我的XPath example in Delphi XE2,而Delphi 2007 update of the XPath example会提供此输出:
nodeName[0]:Number
nodeValue[0]:1
nodeName[1]:Number
nodeValue[1]:225
nodeName[2]:Number
nodeValue[2]:236
示例中的想法应该使用MSXML 6 DOM或OpenXML DOM(Delphi XE2支持MSXML 6 DOM或ADOM XML v4 DOM)来使用Delphi和2007。
请注意,MSXML 6的行为在很大程度上取决于您安装的实际版本,具体取决于您的Windows操作系统(因此this answer)。
这可能意味着您没有安装最新的MS XML 6,或者没有从中导入正确的类型库(Delphi 2007 msxml单元不包含XPath支持所需的IXMLDOMDocument2)。
2012年7月27日,我花了一些时间将这个例子改编成Delphi 2007并进行测试 我不完全确定它完全没有内存泄漏(我将一个TDictionary类混合在一起放入接口),但结果与Delphi XE2示例相同,乍一看它看起来很不错。
在XPathTester
演示应用中,加载示例3,然后运行XPath(将加载您的示例,并执行XPath)。
请注意bo library中的大多数其他内容至少需要Delphi 2009,但这一部分有效 在接下来的几周里,我将按照我在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;
答案 3 :(得分:0)
试试这段代码:
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;
这是结果:
答案 4 :(得分:-1)
Ziena,我改变你的代码:
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;