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
{
     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转换成树形结构,可以很方便的对树进行操作。

 




本文转自麒麟博客园博客,原文链接:http://www.cnblogs.com/zhuqil/archive/2011/07/26/2117559.html,如需转载请自行联系原作者

相关文章
|
iOS开发 开发者
uniapp开发ios打包Error code = -5000 Error message: Error: certificate file(p12) import failed!报错问题如何解决
uniapp开发ios打包Error code = -5000 Error message: Error: certificate file(p12) import failed!报错问题如何解决
974 67
uniapp开发ios打包Error code = -5000 Error message: Error: certificate file(p12) import failed!报错问题如何解决
|
iOS开发 开发者 MacOS
深入探索iOS开发中的SwiftUI框架
【10月更文挑战第21天】 本文将带领读者深入了解Apple最新推出的SwiftUI框架,这一革命性的用户界面构建工具为iOS开发者提供了一种声明式、高效且直观的方式来创建复杂的用户界面。通过分析SwiftUI的核心概念、主要特性以及在实际项目中的应用示例,我们将展示如何利用SwiftUI简化UI代码,提高开发效率,并保持应用程序的高性能和响应性。无论你是iOS开发的新手还是有经验的开发者,本文都将为你提供宝贵的见解和实用的指导。
513 66
|
JavaScript 搜索推荐 Android开发
【01】仿站技术之python技术,看完学会再也不用去购买收费工具了-用python扒一个app下载落地页-包括安卓android下载(简单)-ios苹果plist下载(稍微麻烦一丢丢)-客户的麻将软件需要下载落地页并且要做搜索引擎推广-本文用python语言快速开发爬取落地页下载-优雅草卓伊凡
【01】仿站技术之python技术,看完学会再也不用去购买收费工具了-用python扒一个app下载落地页-包括安卓android下载(简单)-ios苹果plist下载(稍微麻烦一丢丢)-客户的麻将软件需要下载落地页并且要做搜索引擎推广-本文用python语言快速开发爬取落地页下载-优雅草卓伊凡
637 8
【01】仿站技术之python技术,看完学会再也不用去购买收费工具了-用python扒一个app下载落地页-包括安卓android下载(简单)-ios苹果plist下载(稍微麻烦一丢丢)-客户的麻将软件需要下载落地页并且要做搜索引擎推广-本文用python语言快速开发爬取落地页下载-优雅草卓伊凡
|
存储 监控 API
app开发之安卓Android+苹果ios打包所有权限对应解释列表【长期更新】-以及默认打包自动添加权限列表和简化后的基本打包权限列表以uniapp为例-优雅草央千澈
app开发之安卓Android+苹果ios打包所有权限对应解释列表【长期更新】-以及默认打包自动添加权限列表和简化后的基本打包权限列表以uniapp为例-优雅草央千澈
1869 11
|
人工智能 程序员 API
iOS|记一名 iOS 开发新手的前两次 App 审核经历
啥,这玩意也有新手保护期?
492 0
|
开发框架 Android开发 iOS开发
安卓与iOS开发中的跨平台策略:一次编码,多平台部署
在移动应用开发的广阔天地中,安卓和iOS两大阵营各占一方。随着技术的发展,跨平台开发框架应运而生,它们承诺着“一次编码,到处运行”的便捷。本文将深入探讨跨平台开发的现状、挑战以及未来趋势,同时通过代码示例揭示跨平台工具的实际运用。
460 3
|
Java 调度 Android开发
安卓与iOS开发中的线程管理差异解析
在移动应用开发的广阔天地中,安卓和iOS两大平台各自拥有独特的魅力。如同东西方文化的差异,它们在处理多线程任务时也展现出不同的哲学。本文将带你穿梭于这两个平台之间,比较它们在线程管理上的核心理念、实现方式及性能考量,助你成为跨平台的编程高手。
|
XML 数据格式 iOS开发
|
XML iOS开发 数据格式
一个ios工程相关的配置小问题解决介绍,gdata xml解析相关
在我们ios的开发中gdataxml是一个常用的开源实现,很多第三方的库在实现里也会加入它的源码。我们在使用此类库或者直接使用gdataxml库时,切记要在工程中引入libxml2这个框架,否则编译会报错,提示类似下面: Undefined symbols for architecture armv7:   "_xmlDocDumpMemory", referenced from: xxx 引入该框架问题即可解决。
1115 0

相关课程

更多