我在尝试将由MS SQL Server 2014实例返回的XML数据解码为以D7编写的应用程序时遇到问题。(Indy的版本是附带的版本,9.00。 10)。
更新当我最初编写此q时,我的印象是blob字段的内容需要进行Base64解码,但似乎这是错误的。遵循Remy Lebeau的建议,blob流在解码之前包含字段名称和字段值中的可识别文本,但之后不包含。
在下面的代码中,AdoQuery中的SQL只是
从作者中选择*,其中au_lname =' White'对于XML Auto
作者表是演示中的一个' pubs'数据库。我已经添加了" Where"子句限制结果集的大小,以便我可以显示返回的blob的十六进制转储。
根据Sql Server OLH,当' For XML Auto'时,返回数据的默认类型。指定的是二进制base64编码格式'。如果我让IDE创建此字段,AdoQuery的单个字段的数据类型是ftBlob。
执行以下代码会在DecodeToStream"中生成异常"不均匀的大小。在调用IdDecoderMIME.DecodeToString(S)
时,字符串S的长度为3514,3514 mod 4为2,而不是0,因为它显然应该是,因此是例外。我已经确认字段值中的字节数是3514,因此变量的大小和字符串的长度没有区别,即之间没有任何区别。
procedure TForm1.FormCreate(Sender: TObject);
var
SS : TStringStream;
Output : String;
S : String;
IdDecoderMIME : TIdDecoderMIME;
begin
SS := TStringStream.Create('');
IdDecoderMIME := TIdDecoderMIME.Create(Nil);
try
AdoQuery1.Open;
TBlobField(AdoQuery1.Fields[0]).SaveToStream(SS);
S := SS.DataString;
IdDecoderMIME.FillChar := #0;
Output := IdDecoderMIME.DecodeToString(S);
Memo1.Lines.Text := S;
finally
SS.Free;
IdDecoderMIME.Free;
end;
end;
我正在使用此代码:
procedure TForm1.FormCreate(Sender: TObject);
var
SS : TStringStream;
MS : TMemoryStream;
Output : String;
begin
SS := TStringStream.Create('');
MS := TMemoryStream.Create;
try
AdoQuery1.Open;
TBlobField(AdoQuery1.Fields[0]).SaveToStream(SS);
SS.WriteString(#13#10);
Output := SS.DataString;
SS.Position := 0;
MS.CopyFrom(SS, SS.Size);
MS.SaveToFile(ExtractFilePath(Application.ExeName) + 'Blob.txt');
finally
SS.Free;
MS.Free;
end;
end;
Blob.Txt文件的十六进制转储如下所示
00000000 44 05 61 00 75 00 5F 00 69 00 64 00 44 08 61 00 D.a.u._.i.d.D.a.
00000010 75 00 5F 00 6C 00 6E 00 61 00 6D 00 65 00 44 08 u._.l.n.a.m.e.D.
00000020 61 00 75 00 5F 00 66 00 6E 00 61 00 6D 00 65 00 a.u._.f.n.a.m.e.
00000030 44 05 70 00 68 00 6F 00 6E 00 65 00 44 07 61 00 D.p.h.o.n.e.D.a.
00000040 64 00 64 00 72 00 65 00 73 00 73 00 44 04 63 00 d.d.r.e.s.s.D.c.
00000050 69 00 74 00 79 00 44 05 73 00 74 00 61 00 74 00 i.t.y.D.s.t.a.t.
00000060 65 00 44 03 7A 00 69 00 70 00 44 08 63 00 6F 00 e.D.z.i.p.D.c.o.
00000070 6E 00 74 00 72 00 61 00 63 00 74 00 44 07 61 00 n.t.r.a.c.t.D.a.
00000080 75 00 74 00 68 00 6F 00 72 00 73 00 01 0A 02 01 u.t.h.o.r.s.....
00000090 10 E4 04 00 00 0B 00 31 37 32 2D 33 32 2D 31 31 .......172-32-11
000000A0 37 36 02 02 10 E4 04 00 00 05 00 57 68 69 74 65 76.........White
000000B0 02 03 10 E4 04 00 00 07 00 4A 6F 68 6E 73 6F 6E .........Johnson
000000C0 02 04 0D E4 04 00 00 0C 00 34 30 38 20 34 39 36 .........408 496
000000D0 2D 37 32 32 33 02 05 10 E4 04 00 00 0F 00 31 30 -7223.........10
000000E0 39 33 32 20 42 69 67 67 65 20 52 64 2E 02 06 10 932 Bigge Rd....
000000F0 E4 04 00 00 0A 00 4D 65 6E 6C 6F 20 50 61 72 6B ......Menlo Park
00000100 02 07 0D E4 04 00 00 02 00 43 41 02 08 0D E4 04 .........CA.....
正如您所看到的,其中一些是清晰的(字段名称和内容),其中一些不是。有没有人认识到这种格式,并知道如何将其清理成我在SS Management Studio中执行相同查询所获得的纯文本,即如何从结果集中成功提取XML?
顺便说一句,我使用MS OLE DB Provider for Sql Server和Sql Server Native Client 11提供程序,并使用Delphi Seattle代替D7,得到相同的结果(包括Blob.Txt文件的内容)。 / p>
鉴于代码访问外部数据库,此代码是我可以访问MCVE的最接近的代码。
更新#2 如果我将Sql查询更改为
,解码问题就会消失select Convert(Text,
(select * from authors where au_lname = 'White' for xml AUTO
))
给出
的结果(SS
)
<authors au_id="172-32-1176" au_lname="White" au_fname="Johnson" phone="408 496-7223" address="10932 Bigge Rd." city="Menlo Park" state="CA" zip="94025" contract="1"/>
但是我仍然有兴趣知道如何在不需要Convert()的情况下使其工作。我注意到如果从Sql中删除Where子句,返回的内容不是格式良好的XML - 它包含一系列节点,每个数据行一个节点,但没有封闭的根节点。
另外顺便说一下,我意识到我可以通过不使用&#34来避免这个问题;对于XML Auto&#34;,我只是对如何正确地做这件事感兴趣。此外,一旦我设法提取XML,我就不需要任何帮助来解析XML。
答案 0 :(得分:4)
添加TYPE Directive以指定您希望返回XML。
select *
from Authors
where au_lname = 'White'
for xml auto, type
答案 1 :(得分:2)
您不能简单地将二进制blob解码为XML。
您可以使用TADOCommand
并将其输出流定向到XML文档对象,例如:
const
adExecuteStream = 1024;
var
xmlDoc, RecordsAffected: OleVariant;
cmd: TADOCommand;
xmlDoc := CreateOleObject('MSXML2.DOMDocument.3.0'); // or CoDomDocument30.Create;
xmlDoc.async := False;
cmd := TADOCommand.Create(nil);
// specify your connection string
cmd.ConnectionString := 'Provider=SQLOLEDB;Data Source=(local);...';
cmd.CommandType := cmdText;
cmd.CommandText := 'select top 1 * from items for xml auto';
cmd.Properties['Output Stream'].Value := xmlDoc;
cmd.Properties['XML Root'].Value := 'RootNode';
cmd.CommandObject.Execute(RecordsAffected, EmptyParam, adExecuteStream);
xmlDoc.save('d:\test.xml');
cmd.Free;
这会生成一个格式良好的XML,其中包含根节点RootNode
。