iOS开发之将XML转换成树

来源:互联网 时间:1970-01-01

    开发中由于服务端与客户端是两种不同的平台,而且服务端又是老系统,不具备很好的面向对象的性质,所以导致客户端与服务端只好通过一些制定好的xml进行通信。

    在iOS中对XML的解析不像donet这么方便。当然也存在一些很方便的开源类库去调用,但是有些开源的类库显得很笨重。这篇文章我将封装一个简单操作XML转换成树的类方便自己操作:首先通过NSXMLParser从服务端获取XML,它可以一边下载,一边解析,然后转换成树形结构,最后我们可以从树形结构中去取值。

使用NSXMLParser解析XML:

NSXMLParser中主要有三个委托方法来解析XML:

1、parser:didStartElement: 当解析器对象遇到xml的开始标记时,调用这个方法。

2、parser:didEndElement:当解析器对象遇到xml的结束标记时,调用这个方法。

3、parser:foundCharacters:当解析器找到开始标记和结束标记之间的字符时,调用这个方法。

了解了NSXMLParser机制。然后我们来封装解析XML的类:XMLParser。

#import <CoreFoundation/CoreFoundation.h>#import "TreeNode.h"@interface XMLParser : NSObject{ NSMutableArray *stack;}+ (XMLParser *) sharedInstance;- (TreeNode *) parseXMLFromURL: (NSURL *) url;- (TreeNode *) parseXMLFromData: (NSData*) data;@end

shareInstance使用一个单例。

调用parseXMLFromURL方法,需要一个NSURL的参数,返回我们需要的树节点。

调用parseXMLFromData方法,需要一个NSData的参数,返回我们需要的树节点。

在此之前,先定义TreeNode类:

#import <CoreFoundation/CoreFoundation.h>@interface TreeNode : NSObject{ TreeNode *parent; NSMutableArray *children; NSString *key; NSString *leafvalue;}@property (nonatomic, retain) TreeNode *parent;@property (nonatomic, retain) NSMutableArray *children;@property (nonatomic, retain) NSString *key;@property (nonatomic, retain) NSString *leafvalue;@property (nonatomic, readonly) BOOL isLeaf;@property (nonatomic, readonly) BOOL hasLeafValue;@property (nonatomic, readonly) NSArray *keys;@property (nonatomic, readonly) NSArray *allKeys;@property (nonatomic, readonly) NSArray *uniqKeys;@property (nonatomic, readonly) NSArray *uniqAllKeys;@property (nonatomic, readonly) NSArray *leaves;@property (nonatomic, readonly) NSArray *allLeaves;@property (nonatomic, readonly) NSString *dump;+ (TreeNode *) treeNode;- (NSString *) dump;- (void) teardown;// Leaf Utils- (BOOL) isLeaf;- (BOOL) hasLeafValue;- (NSArray *) leaves;- (NSArray *) allLeaves;// Key Utils- (NSArray *) keys; - (NSArray *) allKeys; - (NSArray *) uniqKeys;- (NSArray *) uniqAllKeys;// Search Utils- (TreeNode *) objectForKey: (NSString *) aKey;- (NSString *) leafForKey: (NSString *) aKey;- (NSMutableArray *) objectsForKey: (NSString *) aKey;- (NSMutableArray *) leavesForKey: (NSString *) aKey;- (TreeNode *) objectForKeys: (NSArray *) keys;- (NSString *) leafForKeys: (NSArray *) keys;// Convert Utils- (NSMutableDictionary *) dictionaryForChildren;@end

TreeNode 实现:

#import "TreeNode.h"// String stripper utility macro#define STRIP(X) [X stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]]@implementation [email protected] parent;@synthesize children;@synthesize key;@synthesize leafvalue;#pragma mark Create and Initialize TreeNodes- (TreeNode *) init{ if (self = [super init]) { self.key = nil; self.leafvalue = nil; self.parent = nil; self.children = nil; } return self;}+ (TreeNode *) treeNode{ return [[[self alloc] init] autorelease];}#pragma mark TreeNode type routines- (BOOL) isLeaf{ return (self.children.count == 0);}- (BOOL) hasLeafValue{ return (self.leafvalue != nil);}#pragma mark TreeNode data recovery routines// Return an array of child keys. No recursion- (NSArray *) keys{ NSMutableArray *results = [NSMutableArray array]; for (TreeNode *node in self.children) [results addObject:node.key]; return results;}// Return an array of child keys with depth-first recursion.- (NSArray *) allKeys{ NSMutableArray *results = [NSMutableArray array]; for (TreeNode *node in self.children) { [results addObject:node.key]; [results addObjectsFromArray:node.allKeys]; } return results;}- (NSArray *) uniqArray: (NSArray *) anArray{ NSMutableArray *array = [NSMutableArray array]; for (id object in [anArray sortedArrayUsingSelector:@selector(caseInsensitiveCompare:)]) if (![[array lastObject] isEqualToString:object]) [array addObject:object]; return array;}// Return a sorted, uniq array of child keys. No recursion- (NSArray *) uniqKeys{ return [self uniqArray:[self keys]];}// Return a sorted, uniq array of child keys. With depth-first recursion- (NSArray *) uniqAllKeys{ return [self uniqArray:[self allKeys]];}// Return an array of child leaves. No recursion- (NSArray *) leaves{ NSMutableArray *results = [NSMutableArray array]; for (TreeNode *node in self.children) if (node.leafvalue) [results addObject:node.leafvalue]; return results;}// Return an array of child leaves with depth-first recursion.- (NSArray *) allLeaves{ NSMutableArray *results = [NSMutableArray array]; for (TreeNode *node in self.children) { if (node.leafvalue) [results addObject:node.leafvalue]; [results addObjectsFromArray:node.allLeaves]; } return results;}#pragma mark TreeNode search and retrieve routines// Return the first child that matches the key, searching recursively breadth first- (TreeNode *) objectForKey: (NSString *) aKey{ TreeNode *result = nil; for (TreeNode *node in self.children) if ([node.key isEqualToString: aKey]) { result = node; break; } if (result) return result; for (TreeNode *node in self.children) { result = [node objectForKey:aKey]; if (result) break; } return result;}// Return the first leaf whose key is a match, searching recursively breadth first- (NSString *) leafForKey: (NSString *) aKey{ TreeNode *node = [self objectForKey:aKey]; return node.leafvalue;}// Return all children that match the key, including recursive depth first search.- (NSMutableArray *) objectsForKey: (NSString *) aKey{ NSMutableArray *result = [NSMutableArray array]; for (TreeNode *node in self.children) { if ([node.key isEqualToString: aKey]) [result addObject:node]; [result addObjectsFromArray:[node objectsForKey:aKey]]; } return result;}// Return all leaves that match the key, including recursive depth first search.- (NSMutableArray *) leavesForKey: (NSString *) aKey{ NSMutableArray *result = [NSMutableArray array]; for (TreeNode *node in [self objectsForKey:aKey]) if (node.leafvalue) [result addObject:node.leafvalue]; return result;}// Follow a key path that matches each first found branch, returning object- (TreeNode *) objectForKeys: (NSArray *) keys{ if ([keys count] == 0) return self; NSMutableArray *nextArray = [NSMutableArray arrayWithArray:keys]; [nextArray removeObjectAtIndex:0]; for (TreeNode *node in self.children) { if ([node.key isEqualToString:[keys objectAtIndex:0]]) return [node objectForKeys:nextArray]; } return nil;}// Follow a key path that matches each first found branch, returning leaf- (NSString *) leafForKeys: (NSArray *) keys{ TreeNode *node = [self objectForKeys:keys]; return node.leafvalue;}#pragma mark output utilities// Print out the tree- (void) dumpAtIndent: (int) indent into:(NSMutableString *) outstring{ for (int i = 0; i < indent; i++) [outstring appendString:@"--"]; [outstring appendFormat:@"[%2d] Key: %@ ", indent, key]; if (self.leafvalue) [outstring appendFormat:@"(%@)", STRIP(self.leafvalue)]; [outstring appendString:@"/n"]; for (TreeNode *node in self.children) [node dumpAtIndent:indent + 1 into: outstring];}- (NSString *) dump{ NSMutableString *outstring = [[NSMutableString alloc] init]; [self dumpAtIndent:0 into:outstring]; return [outstring autorelease];}#pragma mark conversion utilities// When you're sure you're the parent of all leaves, transform to a dictionary- (NSMutableDictionary *) dictionaryForChildren{ NSMutableDictionary *results = [NSMutableDictionary dictionary]; for (TreeNode *node in self.children) if (node.hasLeafValue) [results setObject:node.leafvalue forKey:node.key]; return results;}#pragma mark invocation forwarding// Invocation Forwarding lets node act like array- (id)forwardingTargetForSelector:(SEL)sel { if ([self.children respondsToSelector:sel]) return self.children; return nil;}// Extend selector compliance- (BOOL)respondsToSelector:(SEL)aSelector{ if ( [super respondsToSelector:aSelector] ) return YES; if ([self.children respondsToSelector:aSelector]) return YES; return NO;}// Allow posing as NSArray class for children- (BOOL)isKindOfClass:(Class)aClass{ if (aClass == [TreeNode class]) return YES; if ([super isKindOfClass:aClass]) return YES; if ([self.children isKindOfClass:aClass]) return YES; return NO;}#pragma mark cleanup- (void) teardown{ for (TreeNode *node in [[self.children copy] autorelease]) [node teardown]; [self.parent.children removeObject:self]; self.parent = nil;}- (void) dealloc{ self.parent = nil; self.children = nil; self.key = nil; self.leafvalue = nil; [super dealloc];}@end

从上面的代码可以看出,定义了很多方便的方法来获取数据。

1、teardown:清除所有节点

2、isLeaf:判断是否是叶子节点

3、hasLeafValue:判断节点是否有值

4、- (NSArray *) leaves:返回节点的所有一级子节点值

5、- (NSArray *) allLeaves:返回节点的所有子节点的值

6、keys; 返回节点所有一级子节点名称。

7、 allKeys; 返回节点所有子节点名称。8、 uniqKeys;返回节点一级子节点名称,不重复。
9、uniqAllKeys;返回节点子节点名称,不重复。

10、- (TreeNode *) objectForKey:根据节点名称查询节点

11、- (NSString *) leafForKey: (NSString *) aKey:根据节点名称查询出节点的值

12、- (NSMutableArray *) objectsForKey: (NSString *) aKey;根据节点名称查询出所以满足条件的节点

13、- (NSMutableArray *) leavesForKey: (NSString *) aKey;根据节点名称查询出所以满足条件的节点的值

  14、- (TreeNode *) objectForKeys: (NSArray *) keys;:根据节点名称路径查询出第一个满足条件的节点。

   15、- (NSString *) leafForKeys: (NSArray *) keys 根据节点名称路径查询出第一个满足条件的节点的值。

16、- (NSMutableDictionary *) dictionaryForChildren:将树转换成dictionary
 树定义好了,下面实现XMLParser类:

#import "XMLParser.h"@implementation XMLParserstatic XMLParser *sharedInstance = nil;// Use just one parser instance at any time+(XMLParser *) sharedInstance { if(!sharedInstance) { sharedInstance = [[self alloc] init]; } return sharedInstance;}// Parser returns the tree root. You may have to go down one node to the real results- (TreeNode *) parse: (NSXMLParser *) parser{ stack = [NSMutableArray array]; TreeNode *root = [TreeNode treeNode]; root.parent = nil; root.leafvalue = nil; root.children = [NSMutableArray array]; [stack addObject:root]; [parser setDelegate:self]; [parser parse]; [parser release]; // pop down to real root TreeNode *realroot = [[root children] lastObject]; root.children = nil; root.parent = nil; root.leafvalue = nil; root.key = nil; realroot.parent = nil; return realroot;}- (TreeNode *)parseXMLFromURL: (NSURL *) url{ TreeNode *results; NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; NSXMLParser *parser = [[NSXMLParser alloc] initWithContentsOfURL:url]; results = [self parse:parser]; [pool drain]; return results;}- (TreeNode *)parseXMLFromData: (NSData *) data{ TreeNode *results; NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; NSXMLParser *parser = [[NSXMLParser alloc] initWithData:data]; results = [self parse:parser]; [pool drain]; return results;}// Descend to a new element- (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName attributes:(NSDictionary *)attributeDict{ if (qName) elementName = qName; TreeNode *leaf = [TreeNode treeNode]; leaf.parent = [stack lastObject]; [(NSMutableArray *)[[stack lastObject] children] addObject:leaf]; leaf.key = [NSString stringWithString:elementName]; leaf.leafvalue = nil; leaf.children = [NSMutableArray array]; [stack addObject:leaf];}// Pop after finishing element- (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName{ [stack removeLastObject];}// Reached a leaf- (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string{ if (![[stack lastObject] leafvalue]) { [[stack lastObject] setLeafvalue:[NSString stringWithString:string]]; return; } [[stack lastObject] setLeafvalue:[NSString stringWithFormat:@"%@%@", [[stack lastObject] leafvalue], string]];}@end

使用这两个类:

下面看下我们如何使用这个类:

在iis中放下面这个xml:

<?xml version="1.0" encoding="UTF-8"?><Login><LoginResult>True</LoginResult><LoginInfo>恭喜你登录成功</LoginInfo><LastLogin>2011-05-09 12:20</LastLogin><Right><A>1</A><B>1</B><C>0</C></Right></Login>

使用下面代码获取web服务器上的xml,并将xml转换成树:

 NSURL * url = [[NSURL alloc] initWithString:@"http://10.5.23.117:4444/Login.xml"]; TreeNode *node = [parser parseXMLFromURL:url];

获取xml中的登录结果:

 NSString * result = [node leafForKey:@"LoginResult"];

类似xpath去取值:

NSArray *path =[[NSArray alloc]initWithObjects:@"Right",@"A",nil];NSString * result = [node leafForKeys:path];

将xml显示在tableview上:

@implementation [email protected] root;// Each instance of this controller has a separate root, as// descending through the tree produces new roots.- (id) initWithRoot:(TreeNode *) newRoot{ if (self = [super init]) { self.root = newRoot; NSString *s =[newRoot dump]; if (newRoot.key) self.title = newRoot.key; } return self;}- (id)initWithStyle:(UITableViewStyle)style{ self = [super initWithStyle:style]; if (self) { // Custom initialization } return self;}// The number of rows equals the number of children for a node- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{ return [self.root.children count];}// Color code the cells that can be navigated through- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{ UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"generic"]; if (!cell) cell = [[[UITableViewCell alloc] initWithFrame:CGRectZero reuseIdentifier:@"generic"] autorelease]; TreeNode *child = [[self.root children] objectAtIndex:[indexPath row]]; // Set text if (child.hasLeafValue) cell.textLabel.text = [NSString stringWithFormat:@"%@:%@", child.key, child.leafvalue]; else cell.textLabel.text = child.key; // Set color if (child.isLeaf) cell.textLabel.textColor = [UIColor darkGrayColor]; else cell.textLabel.textColor = [UIColor blackColor]; return cell;}// On selection, either push a new controller or show the leaf value- (void)tableView:(UITableView *)tableViewdidSelectRowAtIndexPath:(NSIndexPath *)indexPath{ TreeNode *child = [self.root.children objectAtIndex:[indexPath row]]; if (child.isLeaf) { return; } TreeBrowserController *tbc = [[[TreeBrowserController alloc] initWithRoot:child] autorelease]; [self.navigationController pushViewController:tbc animated:YES];}// These controllers are ephemeral and need dealloc- (void) dealloc{ self.root = nil; [super dealloc];}@end

效果:
 

总结:这篇文章通过封装两个类库,可以从web上很高效获取xml,将xml转换成树形结构,可以很方便的对树进行操作。

作者:朱祁林
出处:http://zhuqil.cnblogs.com
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

 




相关阅读:
Top