iOS开发之将XML转换成树是本文要介绍的内容,开发中由于服务端与客户端是两种不同的平台,而且服务端又是老系统,不具备很好的面向对象的性质,所以导致客户端与服务端只好通过一些制定好的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 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#
从上面的代码可以看出,定义了很多方便的方法来获取数据。
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 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
使用这两个类:
下面看下我们如何使用这个类:
在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中的登录结果:
- 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
效果:
总结:详解iOS开发之将XML转换成树的内容介绍完了,本文通过封装两个类库,可以从web上很高效获取xml,将xml转换成树形结构,可以很方便的对树进行操作。那么希望本文对你有所帮助!请继续阅读详解iOS开发之将XML转换成树 下篇。