mardi 24 janvier 2012

Simplifier libxml

Comme je l'ai mentionné dans mon précédent post, la manipulation XML sur l'iPhone est en open source libxml bibliothèque, qui est une procédure à base de C API. Nous pouvons également utiliser libxml en Cocoa, et si vous avez un oeil vers la réutilisation de votre code sur l'iPhone, il n'est probablement pas une mauvaise idée d'utiliser libxml lieu de NSXML. Cela signifie aussi que je peux vous montrer comment utiliser libxml sans utiliser le SDK de l'iPhone et de violer la NDA.

Pour utiliser libxml dans vos projets de cacao, vous avez juste besoin de faire une chose dans vos paramètres de projet Xcode, qui est d'ajouter /usr/include/libxml2/** Chemins de recherche à votre tête construire des paramètres:
Ceci va dire votre projet où trouver les fichiers d'entête pour libxml.

Maintenant, pour utiliser libxml pour analyser les données XML contenues dans une instance de NSString, Que nous avons eu:
NSString *xml; // string containing XML
Nous devons nous assurer que nous importer le fichier d'entête nécessaire:
#import 
/xmlmemory.h>
Ensuite, nous devons dire à la bibliothèque pour analyser ces données. Etre une bibliothèque de procédures C, libxml ne sait rien NSString, Qui est un objectif-cluster C class, nous devons donc convertir notre NSString dans une chaîne c, comme ceci:
xmlDocPtr doc = xmlParseMemory([xml UTF8String], [xml lengthOfBytesUsingEncoding:NSUTF8StringEncoding]);
Si ce n'était doc succès ne sera pas NULL. Maintenant, comment pouvons-nous obtenir des valeurs de cela? Eh bien, nous pouvons obtenir le nœud racine comme ceci:
xmlNodePtr root = xmlDocGetRootElement(doc);
xmlNodes sont implémentées comme des listes doublement chaînées, une construction que nous n'utilisons pas beaucoup en Objective-C, bien qu'il soit probablement utilisé sous le capot dans la mise en œuvre de certaines des classes de collection. Cette abstraction est différente de la façon dont nous travaillent habituellement en Objective-C. Ordinairement, nous avons un objet qui représente la collection et nous appeler des méthodes sur cet objet de collection pour obtenir les objets qu'il contient. Dans une liste de la vieille école sont liées comme ceci, le xmlNode objet pointé par une xmlNodePtr représente à la fois le nœud lui-même et la collection. Maintenant, il va seulement être un nœud racine dans la plupart des situations, mais regardons comment nous allions arriver à des enfants du nœud racine.

C'est en fait assez facile. Nous déclarons un autre pointeur de nœud et le diriger vers les enfants les nœuds de cette manière:
xmlNodePtr node = root->children;
Maintenant, le pointeur enfants nous donne un seul nœud, mais ce nœud est aussi notre accès à l'ensemble de la fratrie du nœud et des enfants. Pour parcourir tous les nœuds à ce niveau, nous pouvons en boucle comme ceci:
xmlNodePtr cur_node;
for (cur_node = node; cur_node; cur_node = cur_node->next)
{
 // Do something
}
Cette boucle continue jusqu'à ce qu'il arrive à le dernier élément de la liste chaînée. Le dernier élément a la valeur NULL comme son pointeur à côté, alors la boucle s'arrête. De même, nous pouvons en boucle à travers les enfants d'un noeud comme ceci:
xmlNodePtr children;
for (children = node->children; children; children = children->next)
{
 // Do something
}
Pour obtenir le nom d'un nœud, nous venons de regarder les name membre de la struct XMLNode, qui est une chaîne AC. Donc, si nous voulons trouver un nœud avec un nom spécifique chez les enfants d'un nœud, nous ferions comme ceci:
NSString * nameToSearchFor = @ "Id";
XmlNode enfant * = NULL;
pour (enfant = node-> enfants; enfant; l'enfant = enfant-> suivant)
{
if (strcmp ((char *) enfant-> nom, [cStringUsingEncoding aName: NSUTF8StringEncoding]) == 0)
{
/ / Faire quelque chose avec le noeud
}
}
Pour déterminer la valeur d'un noeud - la valeur entre le début et la fin de votre onglet XML comme ceci:
value
C'est un peu plus compliqué, mais pas beaucoup. Pour obtenir sa valeur comme une chaîne:
xmlChar *ret = xmlNodeListGetString(doc, node->children, 1);
Notez que nous passons pas le nœud, mais le pointeur du nœud enfant. C'est parce que le texte entre le début et la fin de compte les balises comme un enfant.

À ce point, je pense que vous pouvez voir comment le mélange d'une procédure API C avec code Objective-C ressemble un peu laid. D'autre part, je pense que Apple a dû avoir de bonnes raisons de ne pas le port NSXML à l'iPhone, sans doute d'avoir à faire avec la performance, mais ce n'est qu'une conjecture de ma part. Alors, comment pouvons-nous faire notre code plus joli sans pour autant imposer une charge supplémentaire significative?

Eh bien, nous pouvons créer une très faible charge en Objective-C cape. Dans les entrailles de la classe, nous utilisons libxml pour la performance, mais ensuite de les convertir vers et à partir objets Objective-C dans notre accesseurs et manipulateurs. Cela nous donne un bon compromis entre performances et la lisibilité, donc notre code peut ressembler à ceci sans ajouter de surcharge de traitement significative:
 NKDLibXMLDocument *doc = [NKDLibXMLDocument documentWithRawXML:result];
NKDLibXMLNode *root = [doc rootNode];
NKDLibXMLNode *user = [root childNamed:@"User"];
NSLog(@"Id: %@", [user valueForChildNamed:@"Id"]);
NSLog(@"FirstName: %@", [user valueForChildNamed:@"FirstName"]);
NSLog(@"LastName: %@", [user valueForChildNamed:@"LastName"]);
N'est-ce pas plus joli? N'est-il pas facile de dire ce qui se passe dans ce code? Nous nous cachons tous les méchancetés loin dans un couple de classes et ensuite ne jamais avoir à y faire face de nouveau. Ouais, c'est le billet.

J'ai encore quelques travaux à faire sur la classe wrapper, mais je vais le poster dans la journée ou deux.

Aucun commentaire: