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

移动开发 iOS
iOS开发本文章将封装一个简单操作XML转换成树的类方便自己操作:首先通过NSXMLParser从服务端获取XML,它可以一边下载,一边解析,然后转换成树形结构,最后我们可以从树形结构中去取值。

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。

 

  1. #import <CoreFoundation/CoreFoundation.h>    
  2. #import "TreeNode.h"    
  3. @interface XMLParser : NSObject   
  4. {    
  5.     NSMutableArray      *stack;    
  6. }    
  7. + (XMLParser *) sharedInstance;    
  8. - (TreeNode *) parseXMLFromURL: (NSURL *) url;    
  9. - (TreeNode *) parseXMLFromData: (NSData*) data;    
  10. @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 

#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类:

  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转换成树:

  1. NSURL * url = [[NSURL alloc] initWithString:@"http://10.5.23.117:4444/Login.xml"];    
  2.   TreeNode *node = [parser parseXMLFromURL:url];  

获取xml中的登录结果:

  1. view sourceprint?NSString * result =  [node leafForKey:@"LoginResult"];  

类似xpath去取值:

  1. NSArray *path =[[NSArray alloc]initWithObjects:@"Right",@"A",nil];      
  2. 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转换成树 

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

总结:详解iOS开发之将XML转换成的内容介绍完了,本文通过封装两个类库,可以从web上很高效获取xml,将xml转换成形结构,可以很方便的对进行操作。那么希望本文对你有所帮助!请继续阅读详解iOS开发之将XML转换成树 下篇

责任编辑:zhaolei 来源: 博客园
相关推荐

2011-08-02 10:08:32

IOS开发 XML

2011-02-25 10:22:03

ibmdwXMLDB2

2011-12-09 21:13:29

iOS

2021-08-31 09:12:18

StringIntLong

2021-07-14 14:50:08

LinuxASCII图片

2021-06-07 17:30:23

LinuxASCII图片转换

2023-12-11 09:00:00

人工智能3D模型

2023-10-20 08:00:00

人工智能MusicGen

2019-09-06 08:00:00

开源技术 语音

2023-11-09 09:00:00

OpenAI人工智能Whisper

2011-08-02 11:07:42

iOS开发 UIWebView

2018-06-22 10:05:04

Arch LinuxDEB软件包

2021-04-02 08:13:22

5G网络物联网电网

2022-07-19 10:53:57

模型算法智能

2010-03-23 10:29:28

Python程序编译转

2021-03-15 08:00:00

音频框架数据

2009-10-26 10:08:44

VB.NET工程转换成

2020-05-29 09:34:28

httphttps网络协议

2022-10-12 09:55:14

xls文件xlsx文件

2017-08-10 14:15:31

Windows10Windows文件转换
点赞
收藏

51CTO技术栈公众号