IXMLHttpRequest.responseXml пуст, без ошибки синтаксического анализа, когда responseText содержит допустимый Xml
я получаю некоторые XML из правительственный веб-сайт:
http://www.bankofcanada.ca/stats/assets/rates_rss/noon/en_all.xml
я использую следующий, довольно простой код:
var
szUrl: string;
http: IXMLHTTPRequest;
begin
szUrl := 'http://www.bankofcanada.ca/stats/assets/rates_rss/noon/en_all.xml';
http := CoXMLHTTP60.Create;
http.open('GET', szUrl, False, '', '');
http.send(EmptyParam);
Assert(http.Status = 200);
Memo1.Lines.Add('HTTP/1.1 '+IntToStr(http.status)+' '+http.statusText);
Memo1.Lines.Add(http.getAllResponseHeaders);
Memo1.Lines.Add(http.responseText);
я не буду показывать все тело, которое возвращается, но оно возвращает действительный xml в responseText
:
HTTP/1.1 200 OK
Cache-Control: max-age=5
Connection: keep-alive
Connection: Transfer-Encoding
Date: Fri, 30 Mar 2012 14:50:50 GMT
Transfer-Encoding: chunked
Content-Type: text/html; charset=UTF-8
Expires: Fri, 30 Mar 2012 14:50:55 GMT
Server: Apache/2.2.16 (Unix) PHP/5.3.3 mod_ssl/2.2.16 OpenSSL/1.0.0d mod_perl/2.0.4 Perl/v5.12.0
X-Powered-By: PHP/5.3.3
<?xml version="1.0" encoding="ISO-8859-1"?>
<rdf:RDF
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns="http://purl.org/rss/1.0/"
xmlns:cb="http://www.cbwiki.net/wiki/index.php/Specification_1.1"
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:dcterms="http://purl.org/dc/terms/"
xmlns:xsi="http://www.w3c.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.w3c.org/1999/02/22-rdf-syntax-ns#rdf.xsd">
<channel rdf:about="http://www.bankofcanada.ca/stats/assets/rates_rss/noon/en_ALL.xml">
<title xml:lang="en">Bank of Canada: Noon Foreign Exchange Rates</title>
<link>http://www.bankofcanada.ca/rates/exchange/noon-rates-5-day/</link>
Хорошо, хорошо,там есть действительный xml. я знаю, что это действует так...ты только посмотри. Но я также знаю, что это действительно, разбирая его:
var
...
szXml: WideString;
doc: DOMDocument60;
begin
...
szXml := http.responseText;
doc.loadXML(szXml);
Assert(doc.parseError.errorCode = 0);
Memo1.Lines.Add('============parsed xml');
Memo1.Lines.Add(doc.xml);
в origianal IXmlHttpRequest
содержит responseXml
собственность. Из MSDN:
представляет тело анализируемого объекта ответа.
если тело сущности ответа не является допустимым XML, это свойство возвращает DOMDocument, который был проанализирован, так что можно получить доступ к ошибке. Это свойство не возвращает сам IXMLDOMParseError, но он доступен из DOMDocument.
в моем случае свойство responseXml существует, так как оно следует:
Assert(http.responseXml <> nil);
и нет ошибки разбора responseText:
doc := http.responseXml as DOMDocument60;
Assert(doc.parseError.errorCode = 0);
как и должно быть, так как xml действителен.
кроме того, что когда я смотрю на http.responseXml
объект документа, он пуст:
Memo1.Lines.Add('============responseXml');
Memo1.Lines.Add(doc.xml);
Is is IXMLHttpRequest (и IXMLServerHttpRequest), возвращающий пустой XML-документ, когда:
- есть xml
- xml действителен
- нет ошибки разбора
в длинной форме:
uses
msxml2_tlb;
procedure TForm1.Button1Click(Sender: TObject);
var
szUrl: string;
http: IXMLHTTPRequest;
doc: DOMDocument60;
begin
szUrl := 'http://www.bankofcanada.ca/stats/assets/rates_rss/noon/en_all.xml';
http := CoXMLHTTP60.Create; //or CoServerXmlHttpRequest.Create
http.open('GET', szUrl, False, '', '');
http.send(EmptyParam);
Assert(http.Status = 200);
doc := http.responseXml as DOMDocument60;
Assert(doc.parseError.errorCode = 0);
ShowMessage('"'+doc.xml+'"');
end;
как сделать XmlHttpRequest
(и что более важно ServerXMLHTTP60
) вести себя как документально?
4 ответов
Яя нашел проблему
я Саша для сохранения ответа http в текстовый файл. После этого я мог бы изменить файл ответов и проинструктировать fiddler, чтобы он обслуживал мои созданные вручную альтернативы, а не переходил на исходный веб-сайт.
после 3 часов возни мне удалось отследить проблему в исходных заголовках http-ответов:
HTTP/1.1 200 OK
Cache-Control: max-age=5
Connection: keep-alive
Connection: Transfer-Encoding
Date: Fri, 30 Mar 2012 14:50:50 GMT
Transfer-Encoding: chunked
Content-Type: text/html; charset=UTF-8
Expires: Fri, 30 Mar 2012 14:50:55 GMT
Server: Apache/2.2.16 (Unix) PHP/5.3.3 mod_ssl/2.2.16 OpenSSL/1.0.0d mod_perl/2.0.4 Perl/v5.12.0
X-Powered-By: PHP/5.3.3
должны быть:
HTTP/1.1 200 OK
Cache-Control: max-age=5
Connection: keep-alive
Connection: Transfer-Encoding
Date: Fri, 30 Mar 2012 14:50:50 GMT
Transfer-Encoding: chunked
Content-Type: text/xml; charset=UTF-8
Expires: Fri, 30 Mar 2012 14:50:55 GMT
Server: Apache/2.2.16 (Unix) PHP/5.3.3 mod_ssl/2.2.16 OpenSSL/1.0.0d mod_perl/2.0.4 Perl/v5.12.0
X-Powered-By: PHP/5.3.3
как только я нашел проблему, я смог вернуться-найти документация, объясняющая поведение:
поддерживаемые типы MIME для MSXML 6.0:
- " text / xml"
- " application / xml"
- или все, что заканчивается " + xml", например " приложение / rss + xml"
RSS-канал, который я получаю, на самом деле лента формата определения ресурсов (RDF), где тип контента должен быть:
application/rdf+xml
использование:
text/html
ошибается на стольких уровнях.
таким образом, поведение, которое я испытываю, является преднамеренным; хотя и расстраивает - так как нет простого способа узнать, если responseXml
"допустимо".
- the
у меня была такая же проблема с YouTube
услуги.
на responseXml
объект зависит от типа содержимого / MIME ответа.
Вы могли бы изучить ответ Content-Type
Эл.G: если http.getResponseHeader('Content-Type')
содержит text/xml
или application/xml
и только потом вы можете обратиться к http.responseXml
, в противном случае он будет пустым (см. MSDN Примечания). Также обратите внимание, что responseXml
функции проверки анализатора всегда отключены для обеспечения безопасности причины.
но на http.responseText
будет всегда есть xml
текст, независимо от того, какой тип контента находится в ответе, поэтому вы можете всегда используйте новый экземпляр DOMDocument
загрузить xml
Эл.г:
...
http := CoXMLHTTP60.Create; // or CoServerXmlHttpRequest.Create
http.open('GET', szUrl, False, '', '');
http.send(EmptyParam);
Assert(http.Status = 200);
doc := CreateOleObject('Msxml2.DOMDocument.6.0') as DOMDocument60;
doc.async := False;
doc.loadXML(http.responseText); // <- load XmlHttpRequest.responseText into DOMDocument60 and use it
Assert(doc.parseError.errorCode = 0);
// do useful things with doc object...
Ну, это работает в Delphi XE и Delphi 7:
procedure TForm1.Button1Click(Sender: TObject);
var
szUrl: string;
http: IXMLHTTPRequest;
doc: {$ifndef UNICODE}WideString{$else}string{$endif};
begin
szUrl := 'http://www.bankofcanada.ca/stats/assets/rates_rss/noon/en_all.xml';
http := CoXMLHTTP60.Create; //or CoServerXmlHttpRequest.Create
http.open('GET', szUrl, False, '', '');
http.setRequestHeader('Content-Type', 'text/xml;charset=UTF-8');
http.send(EmptyParam);
Assert(http.Status = 200);
doc := UTF8Encode(http.responseText);
Memo1.Lines.text := doc;
// ShowMessage('"'+doc.xml+'"');
end;
надеюсь, что это сработает и для вас в Delphi 5. Конечно, любые символы unicode собираются превратиться в ? на вас, в версиях delphi, отличных от unicode.
вы отступаете xml
С DOMDocument
сам объект, но вы должны захватить его с первого узла в дереве документа, например:
doc := http.responseXml as DOMDocument60;
Assert(doc.parseError.errorCode = 0);
ShowMessage('"' + doc.DocumentElement.childNodes.Item(0).xml + '"');
собственные примеры Microsoft в документации для DOMDocument
и xml
показать именно такую логику.