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,

6
  • 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. Commented Oct 6, 2012 at 17:37
  • 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. Commented Oct 6, 2012 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. Commented Oct 6, 2012 at 18:36
  • LoadXMLDocument() merely creates a TXMLDocument and assigns its FileName property. That property does not support URLs, only local file paths. Commented Oct 7, 2012 at 3:04
  • 1
    @TLama: you are relying on a vendor-specific extension of the underlying engine, in this case MSXML on Windows. Commented Oct 7, 2012 at 17:39

1 Answer 1

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;
Sign up to request clarification or add additional context in comments.

3 Comments

+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.
Off course, always I recommend use XPath for this kind of task.
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.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.