将动态 XML 加载到数据集中
Load dynamic XML into dataset
我正在尝试使用 mydataset.loadfromfile("myfile.xml")
将 XML 文件加载到 Delphi 数据集中
问题是我不知道 XML 字段名称以及它有多少个字段。我知道 XML 只有一级,例如:
<DOCTO id ="3892" usuario="2" data="22/12/2015 10:33:22">
<CONDUTA_1>teste conduta 1</CONDUTA_1>
<CONDUTA_2>teste conduta 2</CONDUTA_2>
</DOCTO>
我正在寻找一种方法来加载 xml 而无需之前创建字段。
如您所说,您不能使用 XMLMapper,我已将答案的那部分移至底部。
由于您不能使用 XmlMapper,因此您需要使用 XML 解析器自行解析 XML。
下面的示例使用 XML DOM 解析器,它随 Windows 一起提供,为此您需要 Uses
列表中的 MSXML
。为了说明如何
这样做,我将使用 XML 与你的结构相同但具有抽象节点-
和属性名称,以避免您的 XML 的节点名称和值分散注意力。
<data a1="a" a2="b" a3="c">
<r1>11111</r1>
<r2>22222</r2>
</data>
在一种极端情况下,字段名称可能是 <data>
节点属性的名称 a1、a2、a3,字段值可能是这些属性的值。
LoadFromAttributes
例程中显示了如何获取这些内容。基本上,它加载
XML 到 XMLDoc 对象中,使用 XPath
查询查找 <data>
节点和
他们掌握它的属性来创建数据集字段并设置它们的值。
另一个极端,子节点<r1>
和<r2>
可能是字段名及其
节点文本,字段值。 LoadFromNodes
例程展示了如何做到这一点。
当然,在实践中,XML 的语义可能意味着字段名称
和 values 是两者的混合体,但是你还没有说什么
你的 XML "means",我不得不把它留给你。
TForm1 = class(TForm)
Memo1: TMemo;
CDS1: TClientDataSet;
DataSource1: TDataSource;
DBGrid1: TDBGrid;
btnAttributes: TButton;
btnFromNodes: TButton;
procedure FormCreate(Sender: TObject);
procedure btnAttributesClick(Sender: TObject);
procedure btnFromNodesClick(Sender: TObject);
public
XMLDoc : IXMLDOMDocument;
procedure PrepareCDS;
procedure LoadFromAttributes;
procedure LoadFromNodes;
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
const
scXML =
'<data a1="a" a2="b" a3="c">'#13#10
+ ' <r1>11111</r1>'#13#10
+ ' <r2>22222</r2>'#13#10
+ '</data>';
scRootNodeName = 'data';
procedure TForm1.FormCreate(Sender: TObject);
begin
XmlDoc := CoDOMDocument.Create;
XmlDoc.Async := False;
XMLDoc.loadXML(scXML);
Memo1.Lines.Text := scXML;
end;
procedure TForm1.LoadFromAttributes;
var
PathQuery : String;
NodeList : IXmlDOMNodeList;
Node,
AttrNode : IXmlDomNode;
Attributes : IXMLDOMNamedNodeMap;
Field : TField;
i : Integer;
begin
PrepareCDS;
PathQuery := scRootNodeName;
NodeList := XMLDoc.SelectNodes(PathQuery);
Assert(NodeList.Length > 0);
Node := NodeList.item[0];
Attributes := Node.attributes;
for i := 0 to Attributes.Length - 1 do begin
AttrNode := Attributes.item[i];
Field := TStringField.Create(Self);
Field.Size := 80;
Field.FieldName := AttrNode.nodeName;
Field.DataSet := CDS1;
end;
CDS1.CreateDataSet;
CDS1.Insert;
for i := 0 to Attributes.Length - 1 do begin
AttrNode := Attributes.item[i];
CDS1.Fields[i].Value := AttrNode.nodeValue;
end;
CDS1.Post;
end;
procedure TForm1.LoadFromNodes;
var
PathQuery : String;
NodeList : IXmlDOMNodeList;
Node,
AttrNode : IXmlDomNode;
Field : TField;
i : Integer;
begin
PrepareCDS;
PathQuery := scRootNodeName + '/*';
NodeList := XMLDoc.SelectNodes(PathQuery);
Assert(NodeList.Length > 0);
for i := 0 to NodeList.Length - 1 do begin
Node := NodeList.item[i];
Field := TStringField.Create(Self);
Field.Size := 80;
Field.FieldName := Node.nodeName;
Field.DataSet := CDS1;
end;
CDS1.CreateDataSet;
CDS1.Insert;
for i := 0 to NodeList.Length - 1 do begin
Node := NodeList.item[i];
CDS1.Fields[i].Value := Node.Text;
end;
CDS1.Post;
end;
procedure TForm1.PrepareCDS;
begin
if CDS1.Active then
CDS1.Close;
CDS1.FieldDefs.Clear;
CDS1.Fields.Clear;
end;
procedure TForm1.btnAttributesClick(Sender: TObject);
begin
LoadFromAttributes;
end;
procedure TForm1.btnFromNodesClick(Sender: TObject);
begin
LoadFromNodes;
end;
如您所见,只要稍微熟悉 MS XML 解析器和 XPath 的使用,所有这些都非常简单(尽管在本例中,XML 结构非常简单,您并不真的需要使用 XPath 来获取它)。
在你的情况下,它不适用,但如果你的XML结构是预先固定的,你可以使用Delphi附带的实用程序XmlMapper , 定义映射文件以将其转换为 TClientDataSet 使用的格式。然后,您可以使用
TClientDataSet 与另一个组件 TXmlTransformProvider 结合使用,它使用转换文件将数据加载到 CDS 中。更详细的信息在这里:
顺便说一句,如果您的 .Xml 文件实际上有多个 <DOCTO>
节点,您需要将它们放在根节点下,以便文件具有一个有效的 XML 结构。
我正在尝试使用 mydataset.loadfromfile("myfile.xml")
问题是我不知道 XML 字段名称以及它有多少个字段。我知道 XML 只有一级,例如:
<DOCTO id ="3892" usuario="2" data="22/12/2015 10:33:22">
<CONDUTA_1>teste conduta 1</CONDUTA_1>
<CONDUTA_2>teste conduta 2</CONDUTA_2>
</DOCTO>
我正在寻找一种方法来加载 xml 而无需之前创建字段。
如您所说,您不能使用 XMLMapper,我已将答案的那部分移至底部。
由于您不能使用 XmlMapper,因此您需要使用 XML 解析器自行解析 XML。
下面的示例使用 XML DOM 解析器,它随 Windows 一起提供,为此您需要 Uses
列表中的 MSXML
。为了说明如何
这样做,我将使用 XML 与你的结构相同但具有抽象节点-
和属性名称,以避免您的 XML 的节点名称和值分散注意力。
<data a1="a" a2="b" a3="c">
<r1>11111</r1>
<r2>22222</r2>
</data>
在一种极端情况下,字段名称可能是 <data>
节点属性的名称 a1、a2、a3,字段值可能是这些属性的值。
LoadFromAttributes
例程中显示了如何获取这些内容。基本上,它加载
XML 到 XMLDoc 对象中,使用 XPath
查询查找 <data>
节点和
他们掌握它的属性来创建数据集字段并设置它们的值。
另一个极端,子节点<r1>
和<r2>
可能是字段名及其
节点文本,字段值。 LoadFromNodes
例程展示了如何做到这一点。
当然,在实践中,XML 的语义可能意味着字段名称 和 values 是两者的混合体,但是你还没有说什么 你的 XML "means",我不得不把它留给你。
TForm1 = class(TForm)
Memo1: TMemo;
CDS1: TClientDataSet;
DataSource1: TDataSource;
DBGrid1: TDBGrid;
btnAttributes: TButton;
btnFromNodes: TButton;
procedure FormCreate(Sender: TObject);
procedure btnAttributesClick(Sender: TObject);
procedure btnFromNodesClick(Sender: TObject);
public
XMLDoc : IXMLDOMDocument;
procedure PrepareCDS;
procedure LoadFromAttributes;
procedure LoadFromNodes;
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
const
scXML =
'<data a1="a" a2="b" a3="c">'#13#10
+ ' <r1>11111</r1>'#13#10
+ ' <r2>22222</r2>'#13#10
+ '</data>';
scRootNodeName = 'data';
procedure TForm1.FormCreate(Sender: TObject);
begin
XmlDoc := CoDOMDocument.Create;
XmlDoc.Async := False;
XMLDoc.loadXML(scXML);
Memo1.Lines.Text := scXML;
end;
procedure TForm1.LoadFromAttributes;
var
PathQuery : String;
NodeList : IXmlDOMNodeList;
Node,
AttrNode : IXmlDomNode;
Attributes : IXMLDOMNamedNodeMap;
Field : TField;
i : Integer;
begin
PrepareCDS;
PathQuery := scRootNodeName;
NodeList := XMLDoc.SelectNodes(PathQuery);
Assert(NodeList.Length > 0);
Node := NodeList.item[0];
Attributes := Node.attributes;
for i := 0 to Attributes.Length - 1 do begin
AttrNode := Attributes.item[i];
Field := TStringField.Create(Self);
Field.Size := 80;
Field.FieldName := AttrNode.nodeName;
Field.DataSet := CDS1;
end;
CDS1.CreateDataSet;
CDS1.Insert;
for i := 0 to Attributes.Length - 1 do begin
AttrNode := Attributes.item[i];
CDS1.Fields[i].Value := AttrNode.nodeValue;
end;
CDS1.Post;
end;
procedure TForm1.LoadFromNodes;
var
PathQuery : String;
NodeList : IXmlDOMNodeList;
Node,
AttrNode : IXmlDomNode;
Field : TField;
i : Integer;
begin
PrepareCDS;
PathQuery := scRootNodeName + '/*';
NodeList := XMLDoc.SelectNodes(PathQuery);
Assert(NodeList.Length > 0);
for i := 0 to NodeList.Length - 1 do begin
Node := NodeList.item[i];
Field := TStringField.Create(Self);
Field.Size := 80;
Field.FieldName := Node.nodeName;
Field.DataSet := CDS1;
end;
CDS1.CreateDataSet;
CDS1.Insert;
for i := 0 to NodeList.Length - 1 do begin
Node := NodeList.item[i];
CDS1.Fields[i].Value := Node.Text;
end;
CDS1.Post;
end;
procedure TForm1.PrepareCDS;
begin
if CDS1.Active then
CDS1.Close;
CDS1.FieldDefs.Clear;
CDS1.Fields.Clear;
end;
procedure TForm1.btnAttributesClick(Sender: TObject);
begin
LoadFromAttributes;
end;
procedure TForm1.btnFromNodesClick(Sender: TObject);
begin
LoadFromNodes;
end;
如您所见,只要稍微熟悉 MS XML 解析器和 XPath 的使用,所有这些都非常简单(尽管在本例中,XML 结构非常简单,您并不真的需要使用 XPath 来获取它)。
在你的情况下,它不适用,但如果你的XML结构是预先固定的,你可以使用Delphi附带的实用程序XmlMapper , 定义映射文件以将其转换为 TClientDataSet 使用的格式。然后,您可以使用 TClientDataSet 与另一个组件 TXmlTransformProvider 结合使用,它使用转换文件将数据加载到 CDS 中。更详细的信息在这里:
顺便说一句,如果您的 .Xml 文件实际上有多个 <DOCTO>
节点,您需要将它们放在根节点下,以便文件具有一个有效的 XML 结构。