注意这个例子:是 《开发秘籍》的一个project 名字叫 XML Browser
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类:
1 #import <CoreFoundation/CoreFoundation.h> 2 @interface TreeNode : NSObject 3 { 4 TreeNode *parent; 5 NSMutableArray *children; 6 NSString *key; 7 NSString *leafvalue; 8 } 9 @property (nonatomic, retain) TreeNode *parent; 10 @property (nonatomic, retain) NSMutableArray *children; 11 @property (nonatomic, retain) NSString *key; 12 @property (nonatomic, retain) NSString *leafvalue; 13 @property (nonatomic, readonly) BOOL isLeaf; 14 @property (nonatomic, readonly) BOOL hasLeafValue; 15 @property (nonatomic, readonly) NSArray *keys; 16 @property (nonatomic, readonly) NSArray *allKeys; 17 @property (nonatomic, readonly) NSArray *uniqKeys; 18 @property (nonatomic, readonly) NSArray *uniqAllKeys; 19 @property (nonatomic, readonly) NSArray *leaves; 20 @property (nonatomic, readonly) NSArray *allLeaves; 21 @property (nonatomic, readonly) NSString *dump; 22 + (TreeNode *) treeNode; 23 - (NSString *) dump; 24 - (void) teardown; 25 // Leaf Utils 26 - (BOOL) isLeaf; 27 - (BOOL) hasLeafValue; 28 - (NSArray *) leaves; 29 - (NSArray *) allLeaves; 30 // Key Utils 31 - (NSArray *) keys; 32 - (NSArray *) allKeys; 33 - (NSArray *) uniqKeys; 34 - (NSArray *) uniqAllKeys; 35 // Search Utils 36 - (TreeNode *) objectForKey: (NSString *) aKey; 37 - (NSString *) leafForKey: (NSString *) aKey; 38 - (NSMutableArray *) objectsForKey: (NSString *) aKey; 39 - (NSMutableArray *) leavesForKey: (NSString *) aKey; 40 - (TreeNode *) objectForKeys: (NSArray *) keys; 41 - (NSString *) leafForKeys: (NSArray *) keys; 42 43 // Convert Utils 44 - (NSMutableDictionary *) dictionaryForChildren; 45 @end
TreeNode 实现:
1 #import "TreeNode.h" 2 // String stripper utility macro 3 #define STRIP(X) [X stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]] 4 @implementation TreeNode 5 @synthesize parent; 6 @synthesize children; 7 @synthesize key; 8 @synthesize leafvalue; 9 #pragma mark Create and Initialize TreeNodes 10 - (TreeNode *) init 11 { 12 if (self = [super init]) 13 { 14 self.key = nil; 15 self.leafvalue = nil; 16 self.parent = nil; 17 self.children = nil; 18 } 19 return self; 20 } 21 + (TreeNode *) treeNode 22 { 23 return [[[self alloc] init] autorelease]; 24 } 25 #pragma mark TreeNode type routines 26 - (BOOL) isLeaf 27 { 28 return (self.children.count == 0); 29 } 30 - (BOOL) hasLeafValue 31 { 32 return (self.leafvalue != nil); 33 } 34 #pragma mark TreeNode data recovery routines 35 // Return an array of child keys. No recursion 36 - (NSArray *) keys 37 { 38 NSMutableArray *results = [NSMutableArray array]; 39 for (TreeNode *node in self.children) [results addObject:node.key]; 40 return results; 41 } 42 // Return an array of child keys with depth-first recursion. 43 - (NSArray *) allKeys 44 { 45 NSMutableArray *results = [NSMutableArray array]; 46 for (TreeNode *node in self.children) 47 { 48 [results addObject:node.key]; 49 [results addObjectsFromArray:node.allKeys]; 50 } 51 return results; 52 } 53 - (NSArray *) uniqArray: (NSArray *) anArray 54 { 55 NSMutableArray *array = [NSMutableArray array]; 56 for (id object in [anArray sortedArrayUsingSelector:@selector(caseInsensitiveCompare:)]) 57 if (![[array lastObject] isEqualToString:object]) [array addObject:object]; 58 return array; 59 } 60 // Return a sorted, uniq array of child keys. No recursion 61 62 - (NSArray *) uniqKeys 63 { 64 return [self uniqArray:[self keys]]; 65 } 66 // Return a sorted, uniq array of child keys. With depth-first recursion 67 68 - (NSArray *) uniqAllKeys 69 { 70 return [self uniqArray:[self allKeys]]; 71 } 72 // Return an array of child leaves. No recursion 73 74 - (NSArray *) leaves 75 { 76 NSMutableArray *results = [NSMutableArray array]; 77 for (TreeNode *node in self.children) if (node.leafvalue) [results addObject:node.leafvalue]; 78 return results; 79 } 80 // Return an array of child leaves with depth-first recursion. 81 82 - (NSArray *) allLeaves 83 { 84 NSMutableArray *results = [NSMutableArray array]; 85 for (TreeNode *node in self.children) 86 { 87 if (node.leafvalue) [results addObject:node.leafvalue]; 88 [results addObjectsFromArray:node.allLeaves]; 89 } 90 return results; 91 } 92 #pragma mark TreeNode search and retrieve routines 93 // Return the first child that matches the key, searching recursively breadth first 94 - (TreeNode *) objectForKey: (NSString *) aKey 95 { 96 TreeNode *result = nil; 97 for (TreeNode *node in self.children) 98 if ([node.key isEqualToString: aKey]) 99 { 100 result = node; 101 break; 102 } 103 if (result) return result; 104 for (TreeNode *node in self.children) 105 { 106 result = [node objectForKey:aKey]; 107 if (result) break; 108 } 109 return result; 110 } 111 // Return the first leaf whose key is a match, searching recursively breadth first 112 113 - (NSString *) leafForKey: (NSString *) aKey 114 { 115 TreeNode *node = [self objectForKey:aKey]; 116 return node.leafvalue; 117 } 118 // Return all children that match the key, including recursive depth first search. 119 120 - (NSMutableArray *) objectsForKey: (NSString *) aKey 121 { 122 NSMutableArray *result = [NSMutableArray array]; 123 for (TreeNode *node in self.children) 124 { 125 if ([node.key isEqualToString: aKey]) [result addObject:node]; 126 [result addObjectsFromArray:[node objectsForKey:aKey]]; 127 } 128 return result; 129 } 130 // Return all leaves that match the key, including recursive depth first search. 131 132 - (NSMutableArray *) leavesForKey: (NSString *) aKey 133 { 134 NSMutableArray *result = [NSMutableArray array]; 135 for (TreeNode *node in [self objectsForKey:aKey]) 136 if (node.leafvalue) 137 [result addObject:node.leafvalue]; 138 return result; 139 } 140 // Follow a key path that matches each first found branch, returning object 141 142 - (TreeNode *) objectForKeys: (NSArray *) keys 143 144 { 145 if ([keys count] == 0) return self; 146 NSMutableArray *nextArray = [NSMutableArray arrayWithArray:keys]; 147 [nextArray removeObjectAtIndex:0]; 148 for (TreeNode *node in self.children) 149 { 150 if ([node.key isEqualToString:[keys objectAtIndex:0]]) 151 return [node objectForKeys:nextArray]; 152 } 153 return nil; 154 } 155 // Follow a key path that matches each first found branch, returning leaf 156 157 - (NSString *) leafForKeys: (NSArray *) keys 158 { 159 TreeNode *node = [self objectForKeys:keys]; 160 return node.leafvalue; 161 } 162 #pragma mark output utilities 163 // Print out the tree 164 165 - (void) dumpAtIndent: (int) indent into:(NSMutableString *) outstring 166 { 167 for (int i = 0; i < indent; i++) [outstring appendString:@"--"]; 168 [outstring appendFormat:@"[%2d] Key: %@ ", indent, key]; 169 if (self.leafvalue) [outstring appendFormat:@"(%@)", STRIP(self.leafvalue)]; 170 [outstring appendString:@"\n"]; 171 for (TreeNode *node in self.children) [node dumpAtIndent:indent + 1 into: outstring]; 172 } 173 - (NSString *) dump 174 { 175 NSMutableString *outstring = [[NSMutableString alloc] init]; 176 [self dumpAtIndent:0 into:outstring]; 177 return [outstring autorelease]; 178 } 179 #pragma mark conversion utilities 180 // When you're sure you're the parent of all leaves, transform to a dictionary 181 182 - (NSMutableDictionary *) dictionaryForChildren 183 { 184 NSMutableDictionary *results = [NSMutableDictionary dictionary]; 185 for (TreeNode *node in self.children) 186 if (node.hasLeafValue) [results setObject:node.leafvalue forKey:node.key]; 187 return results; 188 } 189 #pragma mark invocation forwarding 190 // Invocation Forwarding lets node act like array 191 192 - (id)forwardingTargetForSelector:(SEL)sel 193 { 194 if ([self.children respondsToSelector:sel]) return self.children; 195 eturn nil; 196 } 197 // Extend selector compliance 198 - (BOOL)respondsToSelector:(SEL)aSelector 199 200 { 201 if ( [super respondsToSelector:aSelector] ) return YES; 202 if ([self.children respondsToSelector:aSelector]) return YES; 203 return NO; 204 } 205 // Allow posing as NSArray class for children 206 - (BOOL)isKindOfClass:(Class)aClass 207 208 { 209 if (aClass == [TreeNode class]) return YES; 210 if ([super isKindOfClass:aClass]) return YES; 211 if ([self.children isKindOfClass:aClass]) return YES; 212 return NO; 213 } 214 #pragma mark cleanup 215 216 - (void) teardown 217 { 218 for (TreeNode *node in [[self.children copy] autorelease]) [node teardown]; 219 [self.parent.children removeObject:self]; 220 self.parent = nil; 221 } 222 223 - (void) dealloc 224 { 225 self.parent = nil; 226 self.children = nil; 227 self.key = nil; 228 self.leafvalue = nil; 229 [super dealloc]; 230 } 231 @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类:
使用这两个类:
下面看下我们如何使用这个类:
在iis中放下面这个xml:
使用下面代码获取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上:
1 @implementation TreeBrowserController 2 @synthesize root; 3 // Each instance of this controller has a separate root, as 4 // descending through the tree produces new roots. 5 6 - (id) initWithRoot:(TreeNode *) newRoot 7 { 8 if (self = [super init]) 9 { 10 self.root = newRoot; 11 NSString *s =[newRoot dump]; 12 if (newRoot.key) self.title = newRoot.key; 13 } 14 return self; 15 } 16 - (id)initWithStyle:(UITableViewStyle)style 17 { 18 self = [super initWithStyle:style]; 19 if (self) { 20 // Custom initialization 21 } 22 return self; 23 }
1 // The number of rows equals the number of children for a node 2 3 - (NSInteger)tableView:(UITableView *)tableView 4 numberOfRowsInSection:(NSInteger)section 5 { 6 return [self.root.children count]; 7 } 8 // Color code the cells that can be navigated through 9 10 - (UITableViewCell *)tableView:(UITableView *)tableView 11 cellForRowAtIndexPath:(NSIndexPath *)indexPath 12 { 13 UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"generic"]; 14 if (!cell) cell = [[[UITableViewCell alloc] 15 initWithFrame:CGRectZero reuseIdentifier:@"generic"] 16 autorelease]; 17 TreeNode *child = [[self.root children] 18 objectAtIndex:[indexPath row]]; 19 // Set text 20 if (child.hasLeafValue) 21 cell.textLabel.text = [NSString stringWithFormat:@"%@:%@", 22 child.key, child.leafvalue]; 23 else 24 cell.textLabel.text = child.key; 25 // Set color 26 if (child.isLeaf) 27 cell.textLabel.textColor = [UIColor darkGrayColor]; 28 else 29 cell.textLabel.textColor = [UIColor blackColor]; 30 return cell; 31 } 32 // On selection, either push a new controller or show the leaf value 33 34 - (void)tableView:(UITableView *)tableView 35 didSelectRowAtIndexPath:(NSIndexPath *)indexPath 36 37 { 38 TreeNode *child = 39 [self.root.children objectAtIndex:[indexPath row]]; 40 if (child.isLeaf) 41 { 42 return; 43 } 44 TreeBrowserController *tbc = [[[TreeBrowserController alloc] 45 initWithRoot:child] autorelease]; 46 [self.navigationController pushViewController:tbc animated:YES]; 47 } 48 // These controllers are ephemeral and need dealloc 49 50 - (void) dealloc 51 { 52 self.root = nil; 53 [super dealloc]; 54 } 55 @end
效果:
总结:详解iOS开发之将XML转换成树的内容介绍完了,本文通过封装两个类库,可以从web上很高效获取xml,将xml转换成树形结构,可以很方便的对树进行操作。
iOS开发本文章将封装一个简单操作XML转换成树的类方便自己操作:首先通过NSXMLParser从服务端获取XML,它可以一边下载,一边解析,然后转换成树形结构,最后我们可以从树形结构中去取值。
IOS开发之将XML转换成树是介绍的内容;上文所述:很好将xml转换成树,并进行操作,但是忽略了对xml节点上属性的操作,现在让我来修改代码,将属性添加进来。
1、在treenode中加一个类型为NSDictionary的attributeDict用于存放属性。代码如下:
NSDictionary * attributeDict;
2、在中可以在parser:didStartElement:方法中取到属性列表,在其中添加添加下面代码。
leaf.attributeDict = [[NSDictionary alloc] initWithDictionary:attributeDict];
3、修改样例xml。
<?xml version="1.0" encoding="UTF-8"?> <Login> <LoginResult id="1">True</LoginResult> <LoginInfo>OK</LoginInfo> <LastLogin>2011-05-09 12:20</LastLogin> <Right> <A>1</A> <B>1</B> <C>0</C> </Right> </Login>
4、取属性id的值。
TreeNode * resultTreeNode = [node objectForKey:@"LoginResult"]; NSString *result = [resultTreeNode.attributeDict objectForKey:@"id"];