IXMLHttpRequest.responseXml is empty, without parsing error, when responseText contains valid Xml

I am extracting some XML from a government website :

http://www.bankofcanada.ca/stats/assets/rates_rss/noon/en_all.xml 

I use the following rather simple code:

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

i will not display the whole returned body, but it returns a valid xml in 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> 

Good, great, there is xml. I know this is true because ... well, just look at that. But I also know that it is valid by analyzing it:

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

The original IXmlHttpRequest contains the responseXml property. From MSDN:

Represents the body of a parsed object.

If the body of the response object is not valid XML, this property returns the DOMDocument that was parsed so that you can access the error. This property does not return IXMLDOMParseError itself, but it is available from DOMDocument.

In my case, the responseXml property exists, as it should:

 Assert(http.responseXml <> nil); 

And there is no responseText parsing error:

 doc := http.responseXml as DOMDocument60; Assert(doc.parseError.errorCode = 0); 

as it should be, since xml is valid.

Also, when I look at the http.responseXml document http.responseXml , it is empty:

  Memo1.Lines.Add('============responseXml'); Memo1.Lines.Add(doc.xml); 

Is is IXMLHttpRequest (and IXMLServerHttpRequest) returns an empty XML document when:

  • there is xml
  • xml is valid
  • missing parsing error

In long form:

 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; 

How do XmlHttpRequest (and more importantly ServerXMLHTTP60 ) behave as documented?

+7
source share
4 answers

I found a problem

I used Fiddler to save the HTTP response in a text file. After that, I can change the answer file and ask the violinist to use my manual alternatives, rather than go to the original website.

enter image description here

After a 3-hour lesson, I was able to identify the problem in the original headers of the HTTP responses:

 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 

it should be:

 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 

As soon as I found the problem, I was able to return the documentation explaining the behavior :

Supported MIME Types for MSXML 6.0:

  • Text / XML
  • " applications / xml "
  • or anything that ends in " + xml ", for example, " application / rss + xml "

The RSS feed I get is a resource format definition (RDF) channel where the content type is assumed:

 application/rdf+xml 

Their use:

 text/html 

incorrect on many levels.

So, the behavior that I experience is design; although disappointing - because there is no easy way to find out if responseXml "valid".

  • responseXml object will be assigned
  • the parseError object will be assigned
  • parseError.ErrorCode is zero
  • responseXml.documentElement will be nil
+4
source

I had the same problem with YouTube services.

The responseXml object depends on the content type / MIME of the response.
You can check the Content-Type response for example: if http.getResponseHeader('Content-Type') contains text/xml or application/xml only then you can access http.responseXml , otherwise it will be empty (see MSDN Notes ). Also note that responseXml parsing features are always disabled for security reasons.

But , http.responseText will always have xml text, no matter what type of content is in the response, so you can always use a new instance of DOMDocument to load xml for example:

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

Well, this works in Delphi XE and 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; 

I hope this works for you in Delphi 5. Of course, will any Unicode characters be converted? on you, in non-unicode delphi versions.

0
source

You are returning xml from the DOMDocument object DOMDocument , but instead you should grab it from the first node in the document tree, for example:

 doc := http.responseXml as DOMDocument60; Assert(doc.parseError.errorCode = 0); ShowMessage('"' + doc.DocumentElement.childNodes.Item(0).xml + '"'); 

Microsoft's own examples in the documentation for the DOMDocument and xml properties show just that logic.

0
source

All Articles