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

来源:互联网 发布:淘宝买东西返利软件 编辑:程序博客网 时间:2024/06/05 20:42

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

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

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


原创粉丝点击