zoukankan      html  css  js  c++  java
  • 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转换成树形结构,可以很方便的对树进行操作。



    (全文完)


    以下为广告部分

    您部署的HTTPS网站安全吗?

    如果您想看下您的网站HTTPS部署的是否安全,花1分钟时间来 myssl.com 检测以下吧。让您的HTTPS网站变得更安全!

    SSL检测评估

    快速了解HTTPS网站安全情况。

    安全评级(A+、A、A-...)、行业合规检测、证书信息查看、证书链信息以及补完、服务器套件信息、证书兼容性检测等。

    SSL证书工具

    安装部署SSL证书变得更方便。

    SSL证书内容查看、SSL证书格式转换、CSR在线生成、SSL私钥加解密、CAA检测等。

    SSL漏洞检测

    让服务器远离SSL证书漏洞侵扰

    TLS ROBOT漏洞检测、心血漏洞检测、FREAK Attack漏洞检测、SSL Poodle漏洞检测、CCS注入漏洞检测。

    作者:朱祁林 出处:http://zhuqil.cnblogs.com 本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。  
  • 相关阅读:
    微信公众号开发授权和分享模块脚本封装
    给定一个包括 n 个整数的数组 nums 和 一个目标值 target。找出 nums 中的三个整数,使得它们的和与 target 最接近。返回这三个数的和。假定每组输入只存在唯一答案
    给你一个包含 n 个整数的数组 nums,判断 nums 中是否存在三个元素 a,b,c ,使得 a + b + c = 0 ?请你找出所有和为 0 且不重复的三元组
    项目启动报错:关于modals以及node版本相关
    假设业务需要,在页面一屏中一次性展示大量图片(100张),导致img组件同时发起大量的请求,导致浏览器性能被消耗殆尽,页面卡死。怎么优化?
    假设页面左侧有一个列表,点击列表某一项时,将根据当前id发起一个请求,并将响应结果展示在右侧。如果快速多次点击不同列表项,当网络不稳定时,请求返回的顺序与我点击顺序不符,导致展示的结果不是我最后一次点击的对应结果,怎么办?
    有一个按钮,点击后就发起一次请求,我现在要限制每2S只能发起一次请求,怎么办?
    微信小程序引入外部字体(字体图标过大,引入外链)
    Android项目打包遇com.android.builder.internal.aapt.v2.Aapt2Exception: AAPT2 error: check logs for details
    解决reverse改变原数组
  • 原文地址:https://www.cnblogs.com/zhuqil/p/2117559.html
Copyright © 2011-2022 走看看