Converting XML into a tree in iOS development

Source: Internet
Author: User
Tags allkeys

In development, because the server and client are two different platforms, and the server is an old system, they do not have a good object-oriented nature, therefore, the client and the server have to communicate with each other through some well-developed XML.

XML parsing in IOS is not as convenient as donet. Of course, there are some very convenient open-source class libraries to call, but some open-source class libraries seem very cumbersome. In this article, I will encapsulate a simple XML operation into a tree class for my own operation: first, get the XML from the server through nsxmlparser, which can be downloaded, parsed, and then converted to a tree structure, finally, we can take values from the tree structure.

Use nsxmlparser to parse XML:

Nsxmlparser mainly has three delegate methods to parse XML:

1. Parser: didstartelement: This method is called when the parser object encounters an XML start mark.

2. Parser: didendelement: This method is called when the parser object encounters an XML end mark.

3. Parser: foundcharacters: This method is called when the parser finds the character between the start mark and the end mark.

Understand the nsxmlparser mechanism. Then we will encapsulate the XML parsing class: xmlparser.

#import <CoreFoundation/CoreFoundation.h>#import "TreeNode.h"@interface XMLParser : NSObject{NSMutableArray*stack;}+ (XMLParser *) sharedInstance;- (TreeNode *) parseXMLFromURL: (NSURL *) url;- (TreeNode *) parseXMLFromData: (NSData*) data;@end

The specified instance uses a singleton.

To call the parsexmlfromurl method, you need an nsurl parameter to return the required Tree node.

To call the parsexmlfromdata method, you need an nsdata parameter to return the required Tree node.
Before that, define the treenode class:

#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) BOOLisLeaf;@property (nonatomic, readonly) BOOLhasLeafValue;@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 implementation:

#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

From the code above, we can see that many convenient methods are defined to obtain data.

1. teardown: Clear all nodes

2. isleaf: determine whether it is a leaf node

3. hasleafvalue: determines whether a node has a value.

4.-(nsarray *) leaves: return the values of all first-level subnodes of a node.

5.-(nsarray *) allleaves: return the values of all the subnodes of the node.

6. Keys: return the names of all level-1 subnodes of the node.
7. allkeys; return the names of all child nodes of the node.
8. uniqkeys; return The Name Of The subnode at the node level, which is unique.
9. uniqallkeys; return the node subnode name, which is unique.

10.-(treenode *) objectforkey: Query nodes by node name

11.-(nsstring *) leafforkey: (nsstring *) akey: query the node value based on the node name.

12.-(nsmutablearray *) objectsforkey: (nsstring *) akey

13.-(nsmutablearray *) leavesforkey: (nsstring *) akey; query the value of the node that meets the condition based on the node name.

14.-(treenode *) objectforkeys: (nsarray *) keys;: query the first node that meets the conditions based on the node name path.

15.-(nsstring *) leafforkeys: (nsarray *) Keys queries the value of the first node that meets the condition based on the node name path.

16.-(nsmutabledictionary *) dictionaryforchildren: converts a tree to a dictionary
 After the tree is defined, the xmlparser class is implemented below:

#import "XMLParser.h"@implementation XMLParserstatic 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 rootTreeNode *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

Use these two classes:

The following describes how to use this class:

Put the following XML in IIS:

<? XML version = "1.0" encoding = "UTF-8"?> <Login> <loginresult> true </loginresult> <logininfo> congratulations on your logon success </logininfo> <lastlogin> </lastlogin> <right> <A> 1 </> <B> 1 </B> <C> 0 </C> </Right> </login>

Use the following code to obtain the XML on the Web server and convert the XML into a tree:

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

Get the login result in XML:

    NSString * result =  [node leafForKey:@"LoginResult"];

Take values similar to XPath:

NSArray *path =[[NSArray alloc]initWithObjects:@"Right",@"A",nil];NSString * result =  [node leafForKeys:path];

Display XML on 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 *)tableViewdidSelectRowAtIndexPath:(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

Effect:
 

Summary:By encapsulating two class libraries, this article can efficiently retrieve XML from the web, convert XML into a tree structure, and perform operations on the tree easily.

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.