1
votes

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,

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
Thank, Remy for your observation. I was mislead by the sample code on About.com (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
@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
LoadXMLDocument() merely creates a TXMLDocument and assigns its FileName property. That property does not support URLs, only local file paths.Remy Lebeau
@TLama: you are relying on a vendor-specific extension of the underlying engine, in this case MSXML on Windows.Remy Lebeau

1 Answers

5
votes

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;