本人第一次发博文,不当之处请多多见谅。 解析xml的方法有很多种,最简单的是用系统提供的 比如要解析这么个xml, 命名为:trainlog.xml
<?xml version="1.0" encoding="utf-8"?> <ArrayOfAnyType> <anyType xsi:type="JNXL_Struct_ZLGL"><ResourceID>L0000017</ResourceID><Name>Story time</Name><FtpPath>/lgftp/资源库/一年级/L0000017/Story time.wlg</FtpPath><LevelID>1</LevelID><SkillType>1</SkillType><TrainMode>AC</TrainMode><Score>-1</Score><StudyDate/><TimeLength>1</TimeLength></anyType> <anyType xsi:type="JNXL_Struct_ZLGL"><ResourceID>L0000018</ResourceID><Name>Let's sing</Name><FtpPath>/lgftp/资源库/一年级/L0000018/Let's sing.wlg</FtpPath><LevelID>1</LevelID><SkillType>1</SkillType><TrainMode>AC</TrainMode><Score>-1</Score><StudyDate/><TimeLength>0</TimeLength></anyType> </ArrayOfAnyType>
可以用以下的方法直接一个个的解析出来- (void)viewDidLoad { [super viewDidLoad]; ArrayAnyType=[[NSMutableArray alloc] init]; NSXMLParser *xmlRead; NSString *path = [[NSBundle mainBundle] pathForResource:@"trainlog" ofType:@"xml"]; NSFileHandle *file = [NSFileHandle fileHandleForReadingAtPath:path]; NSData *data = [file readDataToEndOfFile];//得到xml文件 [file closeFile]; xmlRead = [[NSXMLParser alloc] initWithData:data];//初始化NSXMLParser对象 [data release]; [xmlRead setDelegate:self];//设置NSXMLParser对象的解析方法代理 BOOL success = [xmlRead parse];//调用代理解析NSXMLParser对象,看解析是否成功 NSLog(@"success %d",success); // Do any additional setup after loading the view, typically from a nib. } //解析器,从两个结点之间读取内容 - (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string { NSLog(@"foundCharacters:%@",string); } //获得结点结尾的值 -(void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName { NSLog(@"foundCharacters:%@",elementName); } -(void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName attributes:(NSDictionary *)attributeDict { NSLog(@"didStartElement: %@",elementName); }
但是面对复杂结构的xml 就不得不自己写相应的解析类了,而怎样使得解析的高效而且准确就是现在要解决的问题,用dom的方式是把文档全部导入内存,然后根据结点去查找,sax方式的话目前没有研究过不知道哪位大神能够解答写,我目前采用的是将xml转化为树,即利用以上系统解析的过程,自己一边创建一棵树,然后来查找树中叶子结点的内容。这个思路也借鉴了之前一些大神的code,这里根据自己的具体情况,做了个demo。现在开始新建一个工程,名字自定义吧,要完成以下四个步骤。
1. 创建TreeNode 类2. 创建XMLParser类
3. 创建自己需要解读出来的类,我这里是AnyTypeCls类
4. 在viewController中实现调用自己写的方法。
下面贴代码了
// TreeNode.h
// // HBXMLParser.h // XMLParserDemo #import <Foundation/Foundation.h> #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.m 代码如下/ /* 将xml转化为树 解析xml的算法思想(利用堆栈进栈出栈的原理) 将从XMLParser得到的数据进行整理 */ #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
//XMLParser.h 代码如下#import <Foundation/Foundation.h> #import <CoreFoundation/CoreFoundation.h> #import "TreeNode.h" @interface XMLParser : NSObject<NSXMLParserDelegate> { NSMutableArray *stack; } + (XMLParser *) sharedInstance; - (TreeNode *) parseXMLFromURL: (NSURL *) url; - (TreeNode *) parseXMLFromData: (NSData*) data; - (TreeNode *) parseXMLFromLocalPath: (NSString *) Xmlpath; @end
//XMLParser.m 代码如下:// // XMLParser.m // XMLParserDemo // // #import "XMLParser.h" #import "TreeNode.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 *)parseXMLFromLocalPath: (NSString *) Xmlpath { TreeNode *results; NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; //NSString *path = [[NSBundle mainBundle] pathForResource:@"trainlog" ofType:@"xml"]; NSFileHandle *file = [NSFileHandle fileHandleForReadingAtPath:Xmlpath]; NSData *data = [file readDataToEndOfFile];//得到xml文件 [file closeFile]; NSXMLParser *parser = [[NSXMLParser alloc] initWithData:data]; //= [[NSXMLParser alloc] init] // [[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]; //NSLog(@"didStartElement %@",elementName); } // Pop after finishing element - (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName { [stack removeLastObject]; //NSLog(@"didEndElement %@",elementName); } // 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]]; //NSLog(@"foundCharacters %@",string); } @end
第三步:解析过程基本上写完了,下面是写自己需要保存到某个类,我这里是// AnyTypeCls.h
#import <Foundation/Foundation.h> @interface AnyTypeCls : NSObject { NSString *type; NSString * ResourceID; NSString *Name; NSString *FtpPath; NSString *LevelID; NSString *SkillType; NSString *TrainMode; NSString *Score; NSString *StudyDate; NSString *TimeLength; /* <Name>Story time</Name><FtpPath>/lgftp/中小学资源库/一年级/声文同步L0000017/Story time.wlg</FtpPath><LevelID>1</LevelID><SkillType>1</SkillType><TrainMode>AC</TrainMode><Score>-1</Score><StudyDate/><TimeLength>1</TimeLength>*/ } @property(nonatomic,retain) NSString *type; @property(nonatomic,retain) NSString * ResourceID; @property(nonatomic,retain) NSString *Name; @property(nonatomic,retain) NSString *FtpPath; @property(nonatomic,retain) NSString *LevelID; @property(nonatomic,retain) NSString *SkillType; @property(nonatomic,retain) NSString *TrainMode; @property(nonatomic,retain) NSString *Score; @property(nonatomic,retain) NSString *StudyDate; @property(nonatomic,retain) NSString *TimeLength; @end
// AnyTypeCls.m 代码清单:
第四步:编写自己的调用函数,将自己的类填充#import "AnyTypeCls.h" @implementation AnyTypeCls @synthesize type; @synthesize ResourceID; @synthesize Name; @synthesize FtpPath; @synthesize LevelID; @synthesize SkillType; @synthesize TrainMode; @synthesize Score; @synthesize StudyDate; @synthesize TimeLength; -(NSString *)description { NSString *des=[[NSString alloc] initWithFormat:@"%@, %@, %@,%@,%@,%@,%@,%@, %@",ResourceID, Name,FtpPath,LevelID,SkillType,TrainMode,Score,StudyDate,TimeLength ]; return des; } @end
//ViewController.h
#import <UIKit/UIKit.h> @class AnyTypeCls; @class XMLParser; @interface ViewController : UIViewController { NSMutableArray *ArrayAnyType; XMLParser *parser; } @property(nonatomic,retain) NSMutableArray *ArrayAnyType; @end
//ViewController.m#import "ViewController.h" #import "AnyTypeCls.h" #import "TreeNode.h" #import "XMLParser.h" @interface ViewController () @end @implementation ViewController @synthesize ArrayAnyType; -(void)viewDidLoad { NSString *filePath=[[NSBundle mainBundle] pathForResource:@"trainlog" ofType:@"xml"]; ArrayAnyType=[[NSMutableArray alloc] init]; // NSURL * url = [[NSURL alloc] initWithString:filePath]; //@"http://10.5.23.117:4444/Login.xml"]; if ([[NSFileManager defaultManager] fileExistsAtPath:filePath]) { NSLog(@"文件存在"); parser=[[XMLParser alloc] init]; TreeNode *node=[parser parseXMLFromLocalPath:filePath]; //[parser parseXMLFromData:data]; NSInteger count=[node.allLeaves count]; for (int i=0; i<count; i+=8) { AnyTypeCls *anytype=[[AnyTypeCls alloc] init]; anytype.ResourceID=[node.allLeaves objectAtIndex:i]; anytype.Name=[node.allLeaves objectAtIndex:i+1]; anytype.FtpPath =[node.allLeaves objectAtIndex:i+2]; anytype.LevelID=[node.allLeaves objectAtIndex:i+3]; anytype.SkillType=[node.allLeaves objectAtIndex:i+4]; anytype.TrainMode=[node.allLeaves objectAtIndex:i+5]; anytype.Score=[node.allLeaves objectAtIndex:i+6]; //anytype.StudyDate=@""; anytype.TimeLength=[node.allLeaves objectAtIndex:i+7]; [ArrayAnyType addObject:anytype]; //NSLog(@"%@",anytype); } //NSLog(@"%@",result); } else { NSLog(@"文件不存在"); //[node debugDescription]; } NSLog(@"%@",[ArrayAnyType objectAtIndex:2]); }