详解iOS开发之将XML转换成树

注意这个例子:是 《开发秘籍》的一个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类:

  1. #import "XMLParser.h"    
  2. @implementation XMLParser    
  3. static XMLParser *sharedInstance = nil;    
  4. // Use just one parser instance at any time    
  5. +(XMLParser *) sharedInstance     
  6. {    
  7.     if(!sharedInstance) {    
  8.         sharedInstance = [[self alloc] init];    
  9.     }    
  10.     return sharedInstance;    
  11. }    
  12. // Parser returns the tree root. You may have to go down one node to the real results    
  13. - (TreeNode *) parse: (NSXMLParser *) parser    
  14. {    
  15.    stack = [NSMutableArray array];    
  16.     TreeNode *root = [TreeNode treeNode];    
  17.     root.parent = nil;    
  18.     root.leafvalue = nil;    
  19.     root.children = [NSMutableArray array];    
  20.     [stack addObject:root];    
  21.     [parser setDelegate:self];    
  22.     [parser parse];    
  23.     [parser release];    
  24.     // pop down to real root    
  25.     TreeNode *realroot = [[root children] lastObject];    
  26.     root.children = nil;    
  27.     root.parent = nil;    
  28.     root.leafvalue = nil;    
  29.     root.key = nil;    
  30.     realroot.parent = nil;    
  31.     return realroot;    
  32. }    
  33.  
  34. - (TreeNode *)parseXMLFromURL: (NSURL *) url    
  35. {       
  36.     TreeNode *results;    
  37.     NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];    
  38.     NSXMLParser *parser = [[NSXMLParser alloc] initWithContentsOfURL:url];    
  39.     results = [self parse:parser];    
  40.     [pool drain];    
  41.     return results;    
  42. }    
  43. - (TreeNode *)parseXMLFromData: (NSData *) data    
  44. {       
  45.     TreeNode *results;    
  46.     NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];    
  47.     NSXMLParser *parser = [[NSXMLParser alloc] initWithData:data];    
  48.     results = [self parse:parser];    
  49.     [pool drain];    
  50.     return results;    
  51. }    
  52. // Descend to a new element    
  53.  
  54. - (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)
  55. namespaceURI qualifiedName:(NSString *)qName attributes:(NSDictionary *)attributeDict    
  56. {    
  57.     if (qName) elementName = qName;    
  58.     TreeNode *leaf = [TreeNode treeNode];    
  59.     leaf.parent = [stack lastObject];    
  60.     [(NSMutableArray *)[[stack lastObject] children] addObject:leaf];    
  61.     leaf.key = [NSString stringWithString:elementName];    
  62.     leaf.leafvalue = nil;    
  63.     leaf.children = [NSMutableArray array];    
  64.     [stack addObject:leaf];    
  65. }    
  66. // Pop after finishing element    
  67.  
  68. - (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName    
  69. {    
  70.     [stack removeLastObject];    
  71. }    
  72. // Reached a leaf    
  73.  
  74. - (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string    
  75. {    
  76.     if (![[stack lastObject] leafvalue])    
  77.     {    
  78.         [[stack lastObject] setLeafvalue:[NSString stringWithString:string]];    
  79.         return;    
  80.     }    
  81.     [[stack lastObject] setLeafvalue:[NSString stringWithFormat:@"%@%@", [[stack lastObject] leafvalue], string]];    
  82. }    
  83. @end  

使用这两个类:

下面看下我们如何使用这个类:

在iis中放下面这个xml:

  1. <?xml version="1.0" encoding="UTF-8"?>    
  2. <Login>    
  3. <LoginResult>True</LoginResult>    
  4. <LoginInfo>恭喜你登录成功</LoginInfo>    
  5. <LastLogin>2011-05-09 12:20</LastLogin>    
  6. <Right>    
  7. <A>1</A>    
  8. <B>1</B>    
  9. <C>0</C>    
  10. </Right>   
  11. </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上:

复制代码
 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转换成树_第1张图片

总结:详解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"]; 


你可能感兴趣的:(详解iOS开发之将XML转换成树)