17.11.2011

Einlesen von XML und Zugriff mit XQuery

Hier wird beschrieben, wie XML-Content, d.h. eine XML-Datei oder eine URL, die XML liefert, eingelesen werden. Danach werden gezielt Werte mittels XQuery ausgelesen. Zu beachten ist, daß der XML-Content in ein NSXMLDocument eingelesen werden und damit Probleme auftreten können, wenn die XML-Daten "zu groß" werden. In diesen Fall sollte ein eventbasierter Parser verwendet werden.

XML-Datei einlesen und ein Element mit XQuery auslesen

Als erstes soll eine XML-Datei vom lokalen Dateisystem eingelesen werden. Als Beispiel wird eine TCX-Datei verwendet, aus der die Länge der Wanderung (DistanceMeters) bestimmt werden soll (So eine Datei kann man einfach bei GPSies exportieren.).

Hier ist ein Ausschnitt einer TCX-Datei zu sehen.

<?xml version="1.0" encoding="UTF-8"?>
<TrainingCenterDatabase xmlns="http://www.garmin.com/xmlschemas/TrainingCenterDatabase/v2" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.garmin.com/xmlschemas/TrainingCenterDatabase/v2 http://www.garmin.com/xmlschemas/TrainingCenterDatabasev2.xsd">
  <Folders>
    <Courses>
      <CourseFolder Name="GPSies">
        <CourseNameRef>
          <Id>Rundwanderung</Id>
        </CourseNameRef>
      </CourseFolder>
    </Courses>
  </Folders>
  <Courses>
    <Course>
      <Name>Rundwanderung</Name>
      <Lap>
        <TotalTimeSeconds>4493.0</TotalTimeSeconds>
        <DistanceMeters>12481.917842577497</DistanceMeters>
        <BeginPosition>
          <LatitudeDegrees>50.21631480</LatitudeDegrees>
          <LongitudeDegrees>8.402616940</LongitudeDegrees>
        </BeginPosition>
        <EndPosition>
          <LatitudeDegrees>50.21724890</LatitudeDegrees>
          <LongitudeDegrees>8.402907420</LongitudeDegrees>
        </EndPosition>
        <Intensity>Active</Intensity>
      </Lap>
      <Track>
        <Trackpoint>
          <Time>2011-11-17T10:44:10Z</Time>
          <Position>
            <LatitudeDegrees>50.21631480</LatitudeDegrees>
            <LongitudeDegrees>8.402616940</LongitudeDegrees>
          </Position>
          <AltitudeMeters>509.00000</AltitudeMeters>
          <DistanceMeters>0.000</DistanceMeters>
        </Trackpoint>
        <Trackpoint>
          <Time>2011-11-17T10:44:23Z</Time>
          <Position>
            <LatitudeDegrees>50.21665140</LatitudeDegrees>
            <LongitudeDegrees>8.402668560</LongitudeDegrees>
          </Position>
          <AltitudeMeters>509.00000</AltitudeMeters>
          <DistanceMeters>37.608</DistanceMeters>
        </Trackpoint>

        ...

      </Track>
    </Course>
  </Courses>
</TrainingCenterDatabase>

Der folgende Programmschnipsel zeigt, wie man eine Datei einliest und den gewünschten Wert ausliest und anzeigt.

NSString *filename = [@"~/Documents/Wanderung.tcx" stringByResolvingSymlinksInPath];
NSError *error = nil;
NSString *xmlContent =
     [NSString stringWithContentsOfFile:filename encoding:NSUTF8StringEncoding error:&error];
NSXMLDocument *xmlDOM =
     [[NSXMLDocument alloc] initWithXMLString:xmlContent options:NSXMLNodePreserveAll error:&error] ;
NSString *xquery = @"/TrainingCenterDatabase/Courses/Course/Lap/DistanceMeters";

NSArray *result = [xmlDOM objectsForXQuery:xquery error:&error];
NSXMLNode *firstNode = [result objectAtIndex:0];
NSLog(@"Length in meters: %@", [firstNode stringValue]);

Nachdem der Dateiname festgelegt wurde, wird der Inhalt der Datei mit stringWithContentsOfFile:encoding:error: in einen NSString eingelesen. Dabei ist darauf zu achten, daß die Datei nicht zu groß ist, weil der Inhalt komplett in den Speicher gelesen wird. Anschließend wird ein NSXMLDocument mit dem NSString initialisiert. Dafür wird die Methode initWithXMLString:options:error verwendet. Aus dem Ausschnitt der TCX-Datei läßt sich der XPath, der in xquery gespeichert wird, ableiten. Die Klasse NSXMLDocument stellt die Methode objectsForXQuery:error zur Verfügung, um die XQuery auszuführen. Die Methode liefert ein NSArray zurück. In dem Beispiel wird einfach das erste Element vom Typ NSXMLNode ausgelesen und anschließend der String-Wert ausgegeben.

Zu beachten ist, daß die Fehlerbehandlung in dem Beispiel weggelassen wurde! Normalerweise sollte man das Error-Element auswerten.

XML-Content von URL lesen und mit XQuery auf Elemente zugreifen

In diesem Beispiel werden die XML-Daten direkt von einer URL gelesen, wieder in einem NSString zwischengespeichert und dann zur Initialisierung eines NSXMLDocuments verwendet. Anschließend wird mit einer XQuery darauf zugegriffen und alle Titel der XML-Daten ausgelesen und auf der Konsole ausgegeben. Als Beispieldaten wurde der Heise-News-Feed verwendet.

Der einzige Unterschied zu dem Beispiel oben ist, daß hier die Methode stringWithContentsOfURL:encoding:error verwendet wurde.

NSError *error = nil;

NSString *xmlContent =
   [NSString stringWithContentsOfURL:[NSURL URLWithString:@"http://www.heise.de/newsticker/heise-atom.xml"] encoding:NSUTF8StringEncoding error:&error];

NSXMLDocument *xmlDOM =
   [[NSXMLDocument alloc] initWithXMLString:xmlContent options:NSXMLNodePreserveAll error:&error] ;
NSString *xquery = [NSString stringWithFormat:@"/feed/entry/title"];
NSArray *result = [xmlDOM objectsForXQuery:xquery error:&error];

NSXMLNode* node;
for (node in result) {
    NSLog(@"%@", [node stringValue]);
}

Ein Ausschnitt aus den XML-Daten ist im Folgenden zu sehen:

<?xml version="1.0" encoding="utf-8"?>

<feed xmlns="http://www.w3.org/2005/Atom">
	<title>heise online News</title>
	<subtitle>Nachrichten nicht nur aus der Welt der Computer</subtitle>
	<link href="http://www.heise.de/newsticker/" />
	<link rel="self" href="http://www.heise.de/newsticker/heise-atom.xml" />
	<updated>2011-11-17T19:31:49+01:00</updated>
	<author>
		<name>heise online</name>
	</author>
	<id>http://www.heise.de/newsticker/</id>



	<entry>
		<title>EU-Parlament macht sich für die Netzneutralität stark</title>
		<link href="http://www.heise.de/newsticker/meldung/EU-Parlament-macht-sich-fuer-die-Netzneutralitaet-stark-1381116.html/from/atom10" />
		<id>http://www.heise.de/newsticker/meldung/EU-Parlament-macht-sich-fuer-die-Netzneutralitaet-stark-1381116.html/from/atom10</id>
		<published>2011-11-17T19:30:00+01:00</published>
		<updated>2011-11-17T19:31:49+01:00</updated>


	</entry>


	<entry>
		<title>Erste Grafikbenchmarks von Nvidias Tegra 3</title>
		<link href="http://www.heise.de/newsticker/meldung/Erste-Grafikbenchmarks-von-Nvidias-Tegra-3-1381018.html/from/atom10" />
		<id>http://www.heise.de/newsticker/meldung/Erste-Grafikbenchmarks-von-Nvidias-Tegra-3-1381018.html/from/atom10</id>
		<published>2011-11-17T19:10:00+01:00</published>
		<updated>2011-11-17T19:53:55+01:00</updated>


	</entry>
</feed>