开发中由于服务端与客户端是两种不同的平台,而且服务端又是老系统,不具备很好的面向对象的性质,所以导致客户端与服务端只好通过一些制定好的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;
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 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中的登录结果:
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
效果:
总结:这篇文章通过封装两个类库,可以从web上很高效获取xml,将xml转换成树形结构,可以很方便的对树进行操作。
(全文完)
以下为广告部分
您部署的HTTPS网站安全吗?
如果您想看下您的网站HTTPS部署的是否安全,花1分钟时间来 myssl.com 检测以下吧。让您的HTTPS网站变得更安全!
快速了解HTTPS网站安全情况。
安全评级(A+、A、A-...)、行业合规检测、证书信息查看、证书链信息以及补完、服务器套件信息、证书兼容性检测等。
安装部署SSL证书变得更方便。
SSL证书内容查看、SSL证书格式转换、CSR在线生成、SSL私钥加解密、CAA检测等。
让服务器远离SSL证书漏洞侵扰
TLS ROBOT漏洞检测、心血漏洞检测、FREAK Attack漏洞检测、SSL Poodle漏洞检测、CCS注入漏洞检测。