8.2 JSON und die URLonauten
Ein solches Format ist beispielsweise JSON (JavaScript Object Notation). Es ist auch für Menschen gut lesbar und benötigt nur geringe Verwaltungsinformationen – das sind die Teile in der Zeichenkette, die zur Strukturierung der Daten dienen. Außerdem ist es einfach zu parsen. Aus diesen Gründen setzen inzwischen sehr viele Webdienste (wie Google, YouTube und Twitter) JSON ein.
Der nächste Abschnitt beschreibt kurz den Aufbau des JSON-Formats, und danach soll anhand eines praktischen Beispiels auch die Anwendung nicht zu kurz kommen.
8.2.1 Das JSON-Format

Ein JSON-Ausdruck ist eine Zeichenkette und kann die folgenden einfachen Werte beschreiben:
- die Konstante null
- die booleschen Konstanten false und true
- vorzeichenbehaftete ganze Zahlen, wie z. B. 1, 3 und –12
- Fließkommazahlen: 3.14159, –22.5 oder 22.34e-12
- durch Anführungszeichen begrenzte Zeichenketten: "Hallo"
Nur mit diesen einfachen Werten könnte ein JSON-Ausdruck auch immer nur einen einzelnen Wert beschreiben. Komplexere Daten lassen sich in Arrays oder Dictionarys unterbringen. Arrays beginnen mit einer öffnenden und enden mit einer schließenden eckigen Klammer. Zwischen diesen Klammern steht eine Liste von JSON-Ausdrücken, die durch Kommas getrennt sind. Zum Beispiel sind [], [1, 2, 3], ["A", "B", "C"] oder ["Fritz", 2, 9.8, true] gültige JSON-Ausdrücke für Arrays, und Sie dürfen sogar Arrays ineinander verschachteln: [[1, 2, 3], false, ["Haus"], [[2, 3], [4, 5]]].
Dictionarys enthalten beliebige Schlüssel-Wert-Paare, die jeweils durch ein Komma getrennt und durch geschweifte Klammern begrenzt werden. Dabei ist jeder Schlüssel eine Zeichenkette, die pro Dictionary nur maximal einmal vorkommen darf. Als Wert dürfen Sie hingegen jeden gültigen JSON-Ausdruck verwenden. Ein einfaches Dictionary sieht beispielsweise so aus: { "name" : "Fritz", "age" : 25, "male" : true }. Natürlich können Sie auch Arrays und Dictionarys nahezu beliebig ineinander verschachteln; z. B. so:
{
"lottozahlen" : [1, 2, 13, 19, 27, 32, 49],
"zusatzzahl" : 22,
"gewinner" : [
{"name" : "Lieschen Müller", "zusatzzahl" : true},
{"name" : "Gustav Gans", "zusatzzahl" : false}
]
}
Listing 8.1 JSON-Ausdruck mit verschachtelten Arrays und Dictionarys
Wie das Beispiel aus Listing 8.1 zeigt, können Sie mit JSON relativ einfach komplexe Datenstrukturen beschreiben.
Mehr zum JSON-Format
Da es hier nicht um die Erstellung eines JSON-Parsers geht, beschränkt sich die Beschreibung des Formats nur auf das Notwendigste. Eine genauere Darstellung finden Sie unter http://www.json.org/json-de.html. Dort finden Sie auch eine formale Beschreibung für JSON-Ausdrücke.
8.2.2 Einfacher YouTube-Zugriff mit JSON

Das Beispielprojekt YouTube dieses Abschnitts führt eine Suche in den YouTube-Videos aus und zeigt das Ergebnis (allerdings nur den Titel und die Beschreibung) in einem Tableview an. YouTube liefert Suchergebnis als JSON-Ausdruck. Wie bereits erwähnt wurde, brauchen Sie dazu keine eigenen JSON-Parser zu schreiben. Stattdessen verwendet das Beispielprojekt fertige Parser.
Projektinformation
Den Quellcode des folgenden Beispiels finden Sie auf der DVD unter Code/Apps/Storyboard/YouTube oder im Github-Repository zum Buch im Unterverzeichnis https://github.com/Cocoaneheads/iPhone/tree/Auflage_2/Apps/Storyboard/YouTube.
Apple bietet seit iOS 5 mit der Klasse NSJSONSerialization die komplette JSON-Serialisierung und -Deserialisierung an. Sie bildet dabei die JSON-Datentypen auf Objective-C-Klassen gemäß der Tabelle 8.2 ab.
| JSON-Datentyp bzw. -Konstante | Objective-C-Klasse |
|
null |
NSNull |
|
false / true |
NSNumber |
|
Ganzzahl |
NSNumber |
|
Fließkommazahl |
NSNumber |
|
Zeichenkette |
NSString |
|
Array |
NSArray |
|
Dictionary |
NSDictionary |
Die Applikation kann nun eine Suche auf YouTube ausführen, das Ergebnis in ein NSData-Objekt laden und es dann mithilfe dieser Methode deserialisieren, um es danach im Tableview darzustellen. Die Suche können Sie über einen URL-Aufruf ausführen. Beispielsweise finden Sie über die URL
http://gdata.youtube.com/feeds/api/videos?orderby=published&alt=json&q=iOS
alle Videos, die den Suchbegriff iOS enthalten. Die URL enthält den Suchbegriff im Parameter q [Das q steht für »query« – das englische Wort für »Suchanfrage«.] am Ende, und über den Parameter alt=json geben Sie an, dass YouTube Ihnen das Ergebnis im JSON-Format liefern soll. Außerdem enthält die URL noch den Parameter orderby=published, der für eine Sortierung des Ergebnisses nach dem Veröffentlichungsdatum sorgt. Wenn Sie den alt-Parameter weglassen, erhalten Sie das Ergebnis stattdessen in einem XML-Format. Wenn Sie diese URL auch in Ihrem Browser eingeben, erhalten Sie eine weiße Seite mit sehr viel Text (siehe Abbildung 8.1).
Abbildung 8.1 Ergebnis einer YouTube-Suche
Wie Sie an der Abbildung sehen können, liefert YouTube Ihnen auf der obersten Ebene ein Dictionary, das unter anderem den Schlüssel «feed« enthält. Unter diesem Schlüssel finden Sie einen weiteren «entry«, der ein Array mit den Einträgen in jeweils einem Dictionary enthält. Die Beispielapplikation braucht also nur den Wert zu diesem Schlüsselpfad über die Methode valueForKeyPath: auszulesen und das Array in einer Property zu speichern. Natürlich verwendet sie für die Datenabfrage keinen Browser, sondern ruft den Inhalt stattdessen über den Convenience-Konstruktor dataWithContentsOfURL: der Klasse NSData von YouTube ab.
Die Methode dataWithContentsOfURL: führt eine synchrone URL-Anfrage – auch synchroner Request genannt – durch. Dabei fragt die Methode die Daten komplett vom Server ab und liefert sie in einem Objekt zurück. Der Aufrufer der Methode muss also so lange warten, bis die Methode die Daten vollständig geladen hat, was bei großen Datenmengen oder langsamen Verbindungen etwas dauern kann. Die Applikation ist während eines solchen Methodenaufrufs in der Regel geblockt und kann keine anderen Operationen ausführen. Dieses Manko lässt sich durch asynchrone Requests umgehen, die allerdings etwas aufwendiger zu implementieren sind. Abschnitt 8.3.5 geht auf die Implementierung asynchroner Requests genauer ein.
Verwenden Sie synchrone Requests nur, wenn die transferierte Datenmenge sehr klein ist. Eine produktive YouTube-App sollte die Suchergebnisse besser mit einem asynchronen Request laden, da sie mehrere Kilobyte groß sind. Die Beispielapplikation ist also hier ein schlechtes Vorbild.
Listing 8.2 enthält die Methode updateItems, die das Suchergebnis vom YouTube-Server lädt und deserialisiert. Sie liest aus dem Dictionary das Array mit den Videos aus und weist es der Property items zu. Dabei liefert die Methode createURL zunächst einfach die oben beschriebene URL zurück.
- (void)updateItems {
NSData *theData = [NSData dataWithContentsOfURL:
self.createURL];
NSError *theError = nil;
NSDictionary *theResult = [NSJSONSerialization 
JSONObjectWithData:theData options:0 
error:&theError];
if(theError == nil) {
self.items = 
[theResult valueForKeyPath:@"feed.entry"];
[self.tableView reloadData];
}
else {
NSLog(@"updateItems %@", theError);
}
}
Listing 8.2 Laden und Deserialisieren der YouTube-Daten
Das Beispielprojekt verwendet ein Storyboard und einen Tableview mit einem Zellprototyp. Damit und mit der Property items lässt sich die Datenquelle für den Tableview relativ einfach implementieren:
- (NSInteger)numberOfSectionsInTableView:
(UITableView *)inTableView {
return 1;
}
- (NSInteger)tableView:(UITableView *)inTableView
numberOfRowsInSection:(NSInteger)inSection {
return [self.items count];
}
- (UITableViewCell *)tableView:(UITableView *)inTableView
cellForRowAtIndexPath:(NSIndexPath *)inIndexPath {
UITableViewCell *theCell = [self.tableView
dequeueReusableCellWithIdentifier:@"Cell"];
NSDictionary *theItem = [self.items
objectAtIndex:inIndexPath.row];
theCell.textLabel.text = [theItem
valueForKeyPath:@"title.$t"];
theCell.detailTextLabel.text =
[theItem valueForKeyPath:@"content.$t"];
return theCell;
}
Listing 8.3 Die Datenquelle für den Tableview der Videos
Der Titel und die Beschreibung liegen jeweils in einem eigenen Dictionary im Eintrag, weswegen Listing 8.3 auch diese Werte über valueForKeyPath: ausliest. Das ist fast der komplette Code, den Sie für die YouTube-Anbindung schreiben müssen. Die Einbindung eines JSON-Feeds lässt sich also relativ einfach bewerkstelligen
Allerdings ist die Applikation jetzt noch nicht besonders komfortabel. Sie müssen sie neu starten, um die Videos zu aktualisieren, und Sie können den Suchbegriff nicht ändern. Glücklicherweise lassen sich diese beiden Funktionen relativ einfach nachrüsten.
Viele iOS-Applikationen verwenden das Bouncen am oberen Rand des Tableviews als Auslöser für eine Aktualisierung. Bouncen bezeichnet dabei das Ziehen des Inhalts über dessen Grenzen hinaus und das anschließende Zurückflitschen, sodass dabei der Tabellenhintergrund sichtbar wird. In der Beispielapplikation soll das Ziehen der Tabellenzellen nach unten die Aktualisierung auslösen. Das soll jedoch nur geschehen, wenn mindestens ein Viertel des Hintergrunds sichtbar ist. Dieser Zustand lässt sich über das Scrollview-Delegate feststellen. Da die Klasse UITableView eine Unterklasse von UIScrollView ist, führt ein Tableview ja auch die Delegate-Methoden des Scrollviews aus.
Für die Abfrage des Bouncings bieten sich die Delegate-Methoden scrollViewDidScroll: und scrollViewDidEndDragging: an. Der Tableview ruft jedoch die Did-Scroll-Methode kontinuierlich während des Scrollens auf. Damit die Applikation nicht ständig die Daten aktualisiert, müssten Sie bei Verwendung dieser Methode eine Sperre gegen zu häufiges Aktualisieren einbauen.
Die Beispielapplikation verwendet hingegen die End-Dragging-Methode, die der Ta-bleview nur aufruft, wenn der Nutzer das Ziehen beendet. Dadurch kann der Nutzer die Aktualisierung nicht zu schnell hintereinander auslösen. Die Implementierung in der App prüft, ob der Nutzer den Inhalt über ein Viertel der Höhe hinausgezogen hat, und löst in diesem Fall die Aktualisierung aus.
- (void)scrollViewDidEndDragging:
(UIScrollView *)inScrollView
willDecelerate:(BOOL)inDecelerate {
CGPoint theOffset = inScrollView.contentOffset;
if(theOffset.y <
-CGRectGetHeight(inScrollView.frame) / 4.0) {
[self updateItems];
}
}
Listing 8.4 Aktualisierung des Tableviews durch Bouncen
Es hakt noch
Wenn Sie nun den Inhalt des Tableviews nach unten ziehen, erscheinen in den meisten Fällen kurz danach neue Einträge in den Tabellenzellen. Allerdings springt der Inhalt jetzt auch nicht mehr sofort nach oben, wenn Sie ihn loslassen. Das liegt am synchronen Request, der die Bildschirmaktualisierung der Applikation für einen kurzen Moment unterbricht. Wie Sie das unterbinden können, erfahren Sie in Abschnitt 8.3.5.
Durch die Aktualisierung verändert sich allerdings in den meisten Fällen nicht der Tabelleninhalt, weil die YouTube-Nutzer ja nicht ständig neue Videos zu dem Suchbegriff hochladen.
Für die Überprüfung der Aktualisierung können Sie jedoch einen häufigeren Begriff verwenden, der auch aus einem häufigen Buchstaben (z. B. »a« oder »e«) bestehen darf. Wenn Sie nun außerdem mit der Aktualisierung einige Minuten warten, sollte der Tableview nun auch tatsächlich neue Einträge anzeigen.
Das Refreshcontrol
Mit iOS 6 hat Apple für die Aktualisierung von Tableviews ein eigenes Control eingeführt, das zum einen das Aussehen vereinheitlicht und zum andern den Implementierungsaufwand vereinfacht. Das UIRefreshControl zeigt beim Herunterziehen einen länger werdenden Reloadbutton und während der Aktualisierung einen Activity-Indicator mit einem optionalen Text (siehe Abbildung 8.2).
Abbildung 8.2 Das Refreshcontrol beim Herunterziehen
Am einfachsten verwenden Sie das zusammen mit einem UITableViewController, bei dem Sie im Interface Builder nur die Verwendung des Refreshcontrols einzuschalten brauchen. Dazu öffnen Sie den Attributinspektor des Tableviewcontrollers und wählen unter der Einstellung Refreshing in der Rubrik Table View Controller den Eintrag Enabled aus. Mit dieser Auswahl zeigt der Inspektor zusätzliche Felder an, mit denen Sie das Aussehen des Controls beeinflussen können. Neben dem Text können Sie hier die Farbe festlegen (siehe Abbildung 8.3).
Projektinformation
Den Quellcode für diese iOS-6-Variante des YouTube-Projekts finden Sie auf der DVD unter Code/Apps/Storyboard/UniversalYouTube oder im Github-Repository zum Buch im Unterverzeichnis https://github.com/Cocoaneheads/iPhone/tree/Auflage_2/Apps/Storyboard/UniversalYouTube.
Abbildung 8.3 Tableviewcontroller mit Refreshcontrol
Wie alle Controls kommuniziert auch das Refreshcontrol über den Target-Action-Mechanismus mit dem Controller; es verwendet für die Auslösung der Aktualisierung das Value-Changed-Ereignis. Sie müssen also dafür im Verbindungsinspektor des Controls von diesem Ereignis eine Verbindung zur gewünschten Actionmethode des Controllers ziehen. Das Control finden Sie zwar nicht auf der Zeichenfläche des Interface Builders, jedoch in der Baumdarstellung unter dem Tableviewcontroller (siehe Abbildung 8.3).
Allerdings hat (zumindest) die erste Version von iOS 6 einen Bug, durch den das Refreshcontrol die Action-Verbindung für das Value-Changed-Ereignis nicht aus dem Storyboard übernimmt. Stattdessen müssen Sie diese Verbindung in der Methode viewDidLoad setzen. Der Code der Beispielapplikation prüft allerdings zunächst, ob das Control nicht doch die Verbindung aus dem Storyboard übernommen hat.
- (void)viewDidLoad {
[super viewDidLoad];
UIRefreshControl *theControl = self.refreshControl;
if([theControl actionsForTarget:self 
forControlEvent:UIControlEventValueChanged] 
== nil) {
[theControl addTarget:self action:@selector(refresh) 
forControlEvents:UIControlEventValueChanged];
}
}
Listing 8.5 Action-Verbindung des Refreshcontrols setzen
Rückwärtskompatibilität
Wenn Sie das Refreshcontrol in einer App einsetzen möchten, die auch unter iOS 5 oder älter laufen soll, sollten Sie es nicht über den Interface Builder konfigurieren. Hier ist eine Konfiguration in der viewDidLoad-Methode des Tableviewcontrollers sinnvoll. Dazu müssen Sie zuerst die Verfügbarkeit prüfen und Refreshcontrol dann gegebenenfalls erstellen und konfigurieren. Danach können Sie es über die Property refreshControl dem Controller zuweisen:
if([self respondsToSelector:@selector(setRefreshControl:)]) {
UIRefreshControl *theControl = 
[[UIRefreshControl alloc] init];
[theControl addTarget:self 
action:@selector(refresh) 
forControlEvents:UIControlEventValueChanged];
self.refreshControl = theControl;
}
Dieser Code funktioniert natürlich nur, wenn Sie ihn mit dem SDK für iOS 6 übersetzen, da der Compiler die Klassen und Methoden kennen muss.
Die Actionmethode muss nun nur noch das Neuladen auslösen; dazu ruft sie im Beispielprojekt einfach die Methode updateItems auf. Außerdem müssen Sie dem Control das Ende des Aktualisierungsprozesses über die Methode endRefreshing mitteilen, damit es den Aktivitätsindikator stoppt und wieder aus der Anzeige des Tableviews verschwindet. Im Beispielprojekt erfolgt dieser Aufruf am Ende der Methode updateItems.
Sie können das Refreshcontrol auch aus dem Programmcode über die Methode beginRefreshing anzeigen lassen, wenn der Nutzer die Aktualisierung nicht durch einen Bounce ausgelöst hat.
8.2.3 URLs erstellen

Wie bereits erwähnt wurde, besteht ein weiteres Manko der Beispielapplikation darin, dass sie einen festen Suchbegriff verwendet. Mit einigen wenigen Änderungen lässt sich jedoch auch hier Abhilfe schaffen. Dazu fügen Sie als Erstes dem Tableview einen Tableheaderview in Form einer Searchbar hinzu, indem Sie diese per Drag & Drop über der Prototypzelle platzieren (siehe Abbildung 8.4).
Abbildung 8.4 Searchbar im Tableview
Legen Sie den Viewcontroller als Delegate dieser Suchleiste fest, indem Sie zu dessen Klasse das Protokoll UISearchBarDelegate hinzufügen und eine Verbindung vom Delegate-Outlet der Leiste zum Viewcontroller-Objekt im Interface Builder ziehen. Außerdem sollten Sie eine Outlet-Property searchBar vom Controller auf die Suchleiste anlegen. Der Code für die Integration ist relativ kurz. Die Applikation muss die Suche starten, wenn der Nutzer einen Suchbegriff eingegeben hat, und sie sollte die Suchleiste mit einem Begriff vorinitialisieren:
- (void)viewDidLoad {
[super viewDidLoad];
self.searchBar.text = @"iOS";
}
- (void)searchBarSearchButtonClicked:
(UISearchBar *)inSearchBar {
[inSearchBar endEditing:YES];
[self updateItems];
}
Listing 8.6 Initialisierung und Ausführung der Suche
Schließlich muss sie die URL für die Suche noch anpassen, indem sie den Suchbegriff für den Parameter q einsetzt. Dabei erzeugt die Methode die Zeichenkette für die URL über stringWithFormat:
- (NSURL *)createURL {
NSString *theQuery = self.searchBar.text;
NSString *theURL = [NSString stringWithFormat:
@"http://gdata.youtube.com/feeds/api/videos?
orderby=published&alt=json&q=%@",
theQuery];
return [NSURL URLWithString:theURL];
}
Listing 8.7 Erzeugung der URL mit variablem Suchbegriff
Sie können jetzt nach unterschiedlichen Begriffen suchen, wenn Sie diese Begriffe in das Suchfeld eingeben. Allerdings funktioniert die Suche nicht korrekt, wenn Sie bestimmte Sonderzeichen (z. B. das Kaufmanns-Und »&«) eingeben. Sie müssen diese Zeichen maskieren, wenn Sie sie in einem URL-Parameter verwenden. Die Klasse NSString bietet zwar die Methode stringByAddingPercentEscapesUsingEncoding: dafür an, die jedoch für komplette URLs gedacht ist. In einer URL trennt das Kaufmanns-Und Parameterzuweisungen voneinander, und somit kodiert diese Methode die Und-Zeichen nicht.
Allerdings bietet das Framework Core Foundation mit der Funktion CFURLCreateStringByAddingPercentEscapes eine Möglichkeit, diese Kodierung umzusetzen. Das Beispielprojekt enthält die Kategorie NSString(URLTools), die auf Basis dieser Funktion die Methode encodedStringForURLWithEncoding: bereitstellt, die als Parameter einen Wert mit dem Typ NSStringEncoding erhält:
- (NSString *)encodedStringForURLWithEncoding:
(NSStringEncoding)inEncoding {
CFStringEncoding theEncoding =
CFStringConvertNSStringEncodingToEncoding(
inEncoding);
CFStringRef theResult =
CFURLCreateStringByAddingPercentEscapes(
kCFAllocatorDefault,
(__bridge CFStringRef)self,
NULL,
(CFStringRef)@"!*'();:@&=+$,/?%#[]",
inEncoding);
return (__bridge_transfer NSString *)theResult;
}
Listing 8.8 Kodierung von Zeichenketten als URL-Parameter
Da Core Foundation allerdings eigene Werte für Kodierungen verwendet, müssen Sie den Parameterwert erst umrechnen, was die Funktion CFStringConvertNSStringEncodingToEncoding übernimmt.
Die Funktion hat fünf Parameter, wobei der erste einen Allokator enthält, mit dem die Funktion neuen Speicher reserviert. Im zweiten Parameter bekommt sie die Zeichenkette zum Kodieren übergeben, und der Wert hierfür ist self, da sich die Methode in einer Kategorie von NSString befindet. Der dritte Parameter enthält die Zeichen, die die Funktion keinesfalls kodieren soll. Der Wert ist NULL, da sie keine Sonderzeichen ausnehmen soll. Der nächste Parameter gibt die Zeichen an, die sie auf jeden Fall kodieren soll, und der letzte Parameter enthält die gewünschte Zeichenkodierung.
Objective-C meets C
In Listing 8.8 kommen die Regeln für die Toll-Free Bridge mit automatischem Referenzenzählen zum Einsatz, die wir in Kapitel 6, »Models, Layer, Animationen«, besprochen haben. Die Parameter 2 bis 4 der Funktion haben den Typ CFStringRef, weswegen ein entsprechender Cast vor den Werten steht. Hier müssen Sie den Modifizierer __bridge verwenden, da der Cast die Eigentümerschaft nicht ändern soll.
Die Methode hält hingegen die Eigentümerschaft theResult. Bei der Umwandlung dieser Referenz in ein Objekt müssen Sie dem ARC-Compiler mitteilen, dass die Methode Eigentümerin des Objekts ist, weswegen Sie hier __bridge_transfer verwenden müssen. Der Compiler legt dadurch und aufgrund des Methodennamens [Rufen Sie sich hierzu noch einmal die Speicherverwaltungsregeln in Erinnerung.] das Objekt in den Autorelease-Pool.
Die Maskierung von Sonderzeichen für die Verwendung in URLs nennt man auch URL-Kodierung. Sie ersetzt dabei die Sonderzeichen durch Escape-Sequenzen. Das sind in diesem Fall jeweils ein Prozentzeichen und eine zweistellige Hexadezimalzahl. [http://de.wikipedia.org/wiki/Hexadezimalsystem] Die Funktion bestimmt also bei allen Sonderzeichen die Zahl oder Zahlenfolge entsprechend der angegebenen Kodierung und setzt dafür die entsprechende Escape-Sequenz beziehungsweise mehrere Escape-Sequenzen ein.
Tabelle 8.3 gibt enthält die URL-Kodierungen für einige Beispielwörter zu unterschiedlichen Zeichenkodierungen. Da ISO Latin 1 nicht das Eurozeichen »€« enthält, liefert die Funktion für die Zeichenkette »10€« NULL zurück.
Die Frage ist nun, welche Kodierung müssen Sie für den URL-Aufruf verwenden? Der RFC 3986 [http://tools.ietf.org/html/rfc3986] empfiehlt die Verwendung von UTF-8; viele Webseiten halten sich indes nicht daran und verwenden stattdessen ISO Latin 1. Im Zweifelsfall hilft hier nur Ausprobieren. Die YouTube-API unterstützt UTF-8, und so können Sie die Methode createURL wie folgt implementieren:
- (NSURL *)createURL {
NSString *theQuery = [self.searchBar.text 
encodedStringForURLWithEncoding:
NSUTF8StringEncoding];
NSString *theURL = [NSString stringWithFormat:
@"http://gdata.youtube.com/feeds/api/videos
?orderby=published&alt=json&q=%@",
theQuery];
return [NSURL URLWithString:theURL];
}
Listing 8.9 URL-Erzeugung mit Parameterkodierung
8.2.4 JSON-Dokumente schreiben

Die Klasse NSJSONSerialization erlaubt über die Klassenmethode dataWithJSONObject:options:error: auch die Erzeugung von JSON-Dokumenten, wobei der erste Parameter das zu serialisierende Objekt enthält. Die Objekthierarchie dieses Objekts muss ein Dictionary oder Array als Wurzel haben und darf nur aus Objekten der Klassen NSString, NSNumber, NSArray, NSDictionary oder NSNull bestehen; andernfalls löst die Methode eine Ausnahme aus. Ob sich ein Objektbaum an diese Struktur hält, können Sie über die Methode isValidJSONObject: prüfen. Auf die explizite Prüfung sollten Sie jedoch möglichst verzichten, da sie natürlich Rechenzeit kostet. Stattdessen sollten Sie lieber beim Aufbau der Hierarchie darauf achten, nur Objekte mit den erlaubten Klassen zu verwenden.
Die Methode erzeugt ein Datenobjekt, das das JSON-Dokument enthält. Wenn Sie es in eine Datei schreiben wollen, können Sie das über die Methode writeToFile:atomically: machen:
NSError *theError = nil;
NSData *theData = [NSJSONSerialization
dataWithJSONObject:theObject options:0 error:&theError];
if(theError) {
// Fehler auswerten
}
else if(![theData writeToFile:thePath atomically:YES]) {
// Fehler beim Schreiben der Datei
}
Listing 8.10 Schreiben eines JSON-Dokuments in eine Datei
Für den Optionsparameter im Serialisierungsaufruf können Sie auch die Konstante NSJSONWritingPrettyPrinted verwenden. Dann fügt die Methode Leerzeichen und Zeilenumbrüche für eine bessere Lesbarkeit ein.
8.2.5 Verwendung des JSONKits als Parser
![]()
Wenn Sie JSON in einer Applikation unter iOS 4 verwenden möchten, steht Ihnen die Klasse NSJSONSerialization nicht zur Verfügung. Es gibt jedoch eine Reihe von Open-Source-Projekten, die Sie ersatzweise einsetzen können. Beispielsweise können Sie das JSONKit von John Engelhart verwenden, das Sie über die URL https://nodeload.github.com/johnezang/JSONKit/zipball/master laden können. Das eigentliche Kit besteht aus einer Header- und einer Implementierungsdatei, die Sie einfach zu Ihren Projekten hinzufügen können.
Leider unterstützt JSONKit noch kein automatisches Referenzenzählen (ARC). Deswegen müssen Sie allerdings nicht das komplette Projekt auf manuelles Referenzenzählen (MRC) umstellen. Sie können stattdessen ARC nur für die Datei JSONKit.m ausschalten. Übersetzter ARC-Code arbeitet ja problemlos mit übersetztem MRC-Code zusammen.
Zum Ausschalten öffnen Sie das Target des Projekts, indem Sie im Projektnavigator das Projekt auswählen und in der Spalte daneben den Eintrag YouTube in der Rubrik TARGETS wählen. Unter dem Reiter Build Phases klappen Sie danach die Rubrik Compile Sources auf, in der Sie die Datei JSONKit.m selektieren. Wenn Sie nun die Taste für den Zeilenvorschub drücken, öffnet sich ein Pop-up mit einer Texteingabe (siehe Abbildung 8.5). Dort können Sie Kommandozeilenargumente für die Übersetzung dieser Datei eingeben. Um ARC auszuschalten, geben Sie den Text -fno-objc-arc ein.
Das JSONKit stellt über die Kategorie NSData(JSONKitDeserializing) Methoden bereit, um JSON-Daten in Objektbäume umzuwandeln. Sie bildet die JSON-Datentypen auf die gleichen Objective-C-Klassen wie die Apple-Implementierung ab (siehe Tabelle 8.2). In der Datei YouTubeViewController.m brauchen Sie nur die Header-Datei JSONKit.h zu importieren und die Zuweisung zu der Variablen theResults zu ändern:
NSDictionary *theResult = [theData
objectFromJSONDataWithParseOptions:JKParseOptionStrict
error:&theError];
Listing 8.11 JSON-Deserialisierung mit dem JSONKit
Abbildung 8.5 ARC für eine einzelne Datei ausschalten
Mit dem JSONKit können Sie natürlich auch JSON-Dokumente schreiben; die Kategorien stellen auch dafür jeweils mehrere Methoden bereit. Am einfachsten können Sie die Methode JSONData verwenden. In Listing 8.10 brauchen Sie also für die Verwendung des JSONKits nur die zweite Zeile durch die folgende auszutauschen:
NSData *theData = [theObject JSONData];
Premature optimization is the root of all evil
Es gibt noch einen weiteren Vorteil, der dafür spricht, das JSONKit zu verwenden: Es ist wesentlich schneller als das Apple-Framework. [http://www.cocoanetics.com/2011/03/json-versus-plist-the-ultimate-showdown/] Bei großen Datenmengen kann der Einsatz des Kits also auch unter iOS 5 oder neueren Versionen Vorteile bringen. Allerdings sollten Sie es erst dann einsetzen, wenn sich die Standardimplementierung als unzureichend für Ihre Zwecke erweist. Die Umstellung ist ja nicht sehr aufwendig.
Ihr Kommentar
Wie hat Ihnen das <openbook> gefallen? Wir freuen uns immer über Ihre freundlichen und kritischen Rückmeldungen.









Jetzt bestellen





