iOS開發之將XML轉換成樹

來源:互聯網
上載者:User

    開發中由於服務端與用戶端是兩種不同的平台,而且服務端又是老系統,不具備很好的物件導向的性質,所以導致用戶端與服務端只好通過一些制定好的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) BOOLisLeaf;@property (nonatomic, readonly) BOOLhasLeafValue;@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 TreeNode@synthesize 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 rootTreeNode *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 TreeBrowserController@synthesize 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轉換成樹形結構,可以很方便的對樹進行操作。

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.