1

I'm trying to parse a MusicBraninz XML file in Delphi XE2 using the following code:

webquery := 'http://www.musicbrainz.org/ws/2/recording/?query='+escape(tracktitle)+'&artist:'+escape(ArtistTitle);

Log('WebQuery: ' + webquery, 0);

begin
  XMLDoc:= TXMLDocument.Create(nil);
  XMLDoc.FileName := webQuery;
  XMLDoc.Active := True;

  Log('Report: ' + XMLDoc.XML.Text, 0);


   StartItemNode := XMLDoc.DocumentElement.ChildNodes.First.ChildNodes.FindNode('release-list') ;

   ANode := StartItemNode;
   repeat
     Result.Album := ANode.ChildNodes['title'].Text;  <-- Access Violation
     Result.Status:= ANode.ChildNodes['status'].Text;

     ANode := ANode.NextSibling;
   until ANode = nil;

end;

The XML file is fetched correctly and looks like what's below:

<metadata xmlns="http://musicbrainz.org/ns/mmd-2.0#" xmlns:ext="http://musicbrainz.org/ns/ext#-2.0">
<recording-list offset="0" count="1">
<recording ext:score="100" id="a399eec1-d45d-4505-b475-ead0da6cad17">
<title>Mestecăniș</title>
<length>359000</length>
<artist-credit>
<name-credit>
<artist id="8fb78a16-0cba-4175-8c92-d9645dfb007d">
<name>Bucovina</name>
<sort-name>Bucovina</sort-name>
</artist>
</name-credit>
</artist-credit>
<release-list>
<release id="22b00afc-86ea-445a-8805-b6bfa33da74e">
<title>Duh</title>
<status>Official</status>
<release-group type="EP" id="4e8fb87c-3760-48c1-a3d7-88e7a2c839fa">
<primary-type>EP</primary-type>
</release-group>
<date>2010</date>
<country>RO</country>
<medium-list>
<track-count>5</track-count>
<medium>
<position>1</position>
<format>CD</format>
<track-list offset="3" count="5">
<track>
<number>4</number>
<title>Mestecăniș</title>
<length>359000</length>
</track>
</track-list>
</medium>
</medium-list>
</release>
</release-list>
</recording>
</recording-list>
</metadata>

My question is: am I doing anything wrong here? All variables are declared and initialized OK.

Thanks,

RRUZ
  • 134,889
  • 20
  • 356
  • 483
Bogdan Botezatu
  • 579
  • 1
  • 9
  • 25
  • 1
    You can't pass a URL to the `TXMLDocument.FileName` property. If you need to parse a remote XML file, you have to download it locally first through other means, like Indy's `TIdHTTP` component. In which case, I would suggest downloading and parsing the XML using a `TStream` and not a file on the HDD. – Remy Lebeau Oct 06 '12 at 17:37
  • Thank, Remy for your observation. I was mislead by the sample code on About.com (http://delphi.about.com/od/internetintranet/ss/xml_rss_read_3.htm) Reading and manipulating XML files and I did not give it much thought. Of course, this costed me big. – Bogdan Botezatu Oct 06 '12 at 18:08
  • @Remy, you don't need to. It's enough to pass the URL to the `LoadXMLDocument` function as I've suggested in my (now deleted) comment and like RRUZ has in his answer. – TLama Oct 06 '12 at 18:36
  • `LoadXMLDocument()` merely creates a `TXMLDocument` and assigns its `FileName` property. That property does not support URLs, only local file paths. – Remy Lebeau Oct 07 '12 at 03:04
  • @Remy, then try to run [`this code`](http://stackoverflow.com/a/12767083/960757) which works for me in Delphi 2009. – TLama Oct 07 '12 at 07:56
  • 1
    @TLama: you are relying on a vendor-specific extension of the underlying engine, in this case MSXML on Windows. – Remy Lebeau Oct 07 '12 at 17:39

1 Answers1

5

You have an access violation because the FindNode method is returning a nil value and you are trying to access of a invalid memory location. In order to use the FindNode method you must check the hierarchy (level) of the nodes to search and then check if the result is not nil.

Try this sample.

  XMLDoc:= LoadXMLDocument(webQuery);
   StartItemNode := XMLDoc.DocumentElement.ChildNodes.FindNode('recording-list');
   if not Assigned(StartItemNode) then exit;
   StartItemNode := StartItemNode.ChildNodes.FindNode('recording');
   if Assigned(StartItemNode) then
   begin
     StartItemNode := StartItemNode.ChildNodes.FindNode('release-list');
     if Assigned(StartItemNode) then
     begin
       StartItemNode := StartItemNode.ChildNodes.FindNode('release');
       if Assigned(StartItemNode) then
       begin
         ANode := StartItemNode;
         repeat
           Result.Album := ANode.ChildNodes['title'].Text;
           Result.Status:= ANode.ChildNodes['status'].Text;
           ANode := ANode.NextSibling;
         until ANode = nil;
       end;
     end;
   end;
RRUZ
  • 134,889
  • 20
  • 356
  • 483
  • 1
    +1, you were faster... Anyway, I think it's better to use DOM and XPath for this. Or a crazy looking `XMLDoc.DocumentElement.ChildNodes.First.ChildNodes.First.ChildNodes.FindNode('release-list')` node selection. And `TIdURI.URLDecode` for URL encoding. – TLama Oct 06 '12 at 16:58
  • 2
    Off course, always I recommend use XPath for this kind of task. – RRUZ Oct 06 '12 at 17:01
  • Thank you, @RRUZ. I have never worked with XML until now and it really got me confused. You saved me a couple of hours with your solution. I appreciate your help. – Bogdan Botezatu Oct 06 '12 at 18:05