詳解iOS開(kāi)發(fā)之將XML轉(zhuǎn)換成樹(shù) 上篇
iOS開(kāi)發(fā)之將XML轉(zhuǎn)換成樹(shù)是本文要介紹的內(nèi)容,開(kāi)發(fā)中由于服務(wù)端與客戶端是兩種不同的平臺(tái),而且服務(wù)端又是老系統(tǒng),不具備很好的面向?qū)ο蟮男再|(zhì),所以導(dǎo)致客戶端與服務(wù)端只好通過(guò)一些制定好的xml進(jìn)行通信。
在iOS中對(duì)XML的解析不像donet這么方便。當(dāng)然也存在一些很方便的開(kāi)源類庫(kù)去調(diào)用,但是有些開(kāi)源的類庫(kù)顯得很笨重。本文章將封裝一個(gè)簡(jiǎn)單操作XML轉(zhuǎn)換成樹(shù)的類方便自己操作:首先通過(guò)NSXMLParser從服務(wù)端獲取XML,它可以一邊下載,一邊解析,然后轉(zhuǎn)換成樹(shù)形結(jié)構(gòu),最后我們可以從樹(shù)形結(jié)構(gòu)中去取值。
使用NSXMLParser解析XML:
NSXMLParser中主要有三個(gè)委托方法來(lái)解析XML:
1、parser:didStartElement: 當(dāng)解析器對(duì)象遇到xml的開(kāi)始標(biāo)記時(shí),調(diào)用這個(gè)方法。
2、parser:didEndElement:當(dāng)解析器對(duì)象遇到xml的結(jié)束標(biāo)記時(shí),調(diào)用這個(gè)方法。
3、parser:foundCharacters:當(dāng)解析器找到開(kāi)始標(biāo)記和結(jié)束標(biāo)記之間的字符時(shí),調(diào)用這個(gè)方法。
了解了NSXMLParser機(jī)制。然后我們來(lái)封裝解析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使用一個(gè)單例。
調(diào)用parseXMLFromURL方法,需要一個(gè)NSURL的參數(shù),返回我們需要的樹(shù)節(jié)點(diǎn)。
調(diào)用parseXMLFromData方法,需要一個(gè)NSData的參數(shù),返回我們需要的樹(shù)節(jié)點(diǎn)。
在此之前,先定義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 實(shí)現(xiàn):
- #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;
- eturn 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
#p#
從上面的代碼可以看出,定義了很多方便的方法來(lái)獲取數(shù)據(jù)。
1、teardown:清除所有節(jié)點(diǎn)
2、isLeaf:判斷是否是葉子節(jié)點(diǎn)
3、hasLeafValue:判斷節(jié)點(diǎn)是否有值
4、- (NSArray *) leaves:返回節(jié)點(diǎn)的所有一級(jí)子節(jié)點(diǎn)值
5、- (NSArray *) allLeaves:返回節(jié)點(diǎn)的所有子節(jié)點(diǎn)的值
6、keys; 返回節(jié)點(diǎn)所有一級(jí)子節(jié)點(diǎn)名稱。
7、 allKeys; 返回節(jié)點(diǎn)所有子節(jié)點(diǎn)名稱。
8、 uniqKeys;返回節(jié)點(diǎn)一級(jí)子節(jié)點(diǎn)名稱,不重復(fù)。
9、uniqAllKeys;返回節(jié)點(diǎn)子節(jié)點(diǎn)名稱,不重復(fù)。
10、- (TreeNode *) objectForKey:根據(jù)節(jié)點(diǎn)名稱查詢節(jié)點(diǎn)
11、- (NSString *) leafForKey: (NSString *) aKey:根據(jù)節(jié)點(diǎn)名稱查詢出節(jié)點(diǎn)的值
12、- (NSMutableArray *) objectsForKey: (NSString *) aKey;根據(jù)節(jié)點(diǎn)名稱查詢出所以滿足條件的節(jié)點(diǎn)
13、- (NSMutableArray *) leavesForKey: (NSString *) aKey;根據(jù)節(jié)點(diǎn)名稱查詢出所以滿足條件的節(jié)點(diǎn)的值
14、- (TreeNode *) objectForKeys: (NSArray *) keys;:根據(jù)節(jié)點(diǎn)名稱路徑查詢出第一個(gè)滿足條件的節(jié)點(diǎn)。
15、- (NSString *) leafForKeys: (NSArray *) keys 根據(jù)節(jié)點(diǎn)名稱路徑查詢出第一個(gè)滿足條件的節(jié)點(diǎn)的值。
16、- (NSMutableDictionary *) dictionaryForChildren:將樹(shù)轉(zhuǎn)換成dictionary樹(shù)定義好了,下面實(shí)現(xiàn)XMLParser類:
- #import "XMLParser.h"
- @implementation XMLParser
- static 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
使用這兩個(gè)類:
下面看下我們?nèi)绾问褂眠@個(gè)類:
在iis中放下面這個(gè)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服務(wù)器上的xml,并將xml轉(zhuǎn)換成樹(shù):
- NSURL * url = [[NSURL alloc] initWithString:@"http://10.5.23.117:4444/Login.xml"];
- TreeNode *node = [parser parseXMLFromURL:url];
獲取xml中的登錄結(jié)果:
- view sourceprint?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 *)tableView
- didSelectRowAtIndexPath:(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
效果:
總結(jié):詳解iOS開(kāi)發(fā)之將XML轉(zhuǎn)換成樹(shù)的內(nèi)容介紹完了,本文通過(guò)封裝兩個(gè)類庫(kù),可以從web上很高效獲取xml,將xml轉(zhuǎn)換成樹(shù)形結(jié)構(gòu),可以很方便的對(duì)樹(shù)進(jìn)行操作。那么希望本文對(duì)你有所幫助!請(qǐng)繼續(xù)閱讀詳解iOS開(kāi)發(fā)之將XML轉(zhuǎn)換成樹(shù) 下篇。