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;
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转换成树形结构,可以很方便的对树进行操作。
开发中由于服务端与客户端是两种不同的平台,而且服务端又是老系统,不具备很好的面向对象的性质,所以导致客户端与服务端只好通过一些制定好的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
{
|

