Simple and concise desktop Cocoa NSXMLParser examp

2019-03-01 00:43发布

问题:

I would like to look through the elements of a file and when one specific element comes out, output the contents in between the tag.

I tried to follow the example in the Mac Dev entitled Event Driven XML Programming, but it just doesn't finish very clearly. It says to make sure I code the delegates, but it never shows an example. I just want to see a simple example where:

  • The file is assumed to be a good xml file.
  • Its path is a URL (or string).
  • The way the delegate interacts with the parser is explained.

Many tutorials for Cocoa seem to almost teach you to circumvent the delegate classes and make your own IBAction functions so I'm missing the training I think on how to use the delegates properly. Its not clear in the example if I'm supposed to build the delegates in the delegate class or keep them in the class with the parser.

回答1:

This is based on something I originally wrote for Cut out a part of a long NSString. I copied the NSXMLParserDelegate code from that iOS project into an OS X project. It gets the text from a specific object in a web page.

.h file:

@interface so7576593AppDelegate : NSObject <NSApplicationDelegate, NSXMLParserDelegate> {
    NSWindow *window;
    IBOutlet NSTextField *textField;

    NSMutableString *divCharacters;
    BOOL captureCharacters; 
}

@property (assign) IBOutlet NSWindow *window;

@end

.m file:

#import "so7576593AppDelegate.h"

@implementation so7576593AppDelegate

@synthesize window;

- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
    captureCharacters = NO;
    NSURL *theURL = [NSURL URLWithString:@"http://maxnerios.yolasite.com/"];
    NSXMLParser *parser = [[NSXMLParser alloc] initWithContentsOfURL:theURL];
    [parser setDelegate:self];
    [parser parse];
    [parser release];

}

- (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qualifiedName attributes:(NSDictionary *)attributeDict {
    if ([elementName isEqual:@"div"] && [[attributeDict objectForKey:@"id"] isEqual:@"I3_sys_txt"]) {
        captureCharacters = YES;
        divCharacters = [[NSMutableString alloc] initWithCapacity:500];
    }
}

- (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string {
    if (captureCharacters) {
        //from parser:foundCharacters: docs:
        //The parser object may send the delegate several parser:foundCharacters: messages to report the characters of an element. 
        //Because string may be only part of the total character content for the current element, you should append it to the current 
        //accumulation of characters until the element changes.
        [divCharacters appendString:string];
    }
}

- (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName {
    if (captureCharacters) {
        captureCharacters = NO;
        [textField setStringValue:divCharacters];
        [divCharacters release];
    }
}

@end 


回答2:

If you click the "Next" link on that page and go onto "Handling XML Elements and Attributes" it will give you an example of how to code the delegates.

Apple provides a Mac example in ImageMap.

There's no difference between NSXMLParser on Mac and iPhone, so reading an iPhone example shouldn't be a problem.



回答3:

Here is an example of using an NSXMLParser in a custom class that takes in a string of the tag to look for and the xml NSData:

JHXMLParser.h:

@protocol JHXMLParserDelegate <NSObject>
@optional
- (void)acceptParsedData:(NSMutableArray *)parsedData withIdent:(NSString *)ident;
- (void)acceptParsedDebugData:(NSMutableArray *)parsedData withIdent:(NSString *)ident;

@end

@class JHKeyValuePair;

@interface JHXMLParser : NSObject <NSXMLParserDelegate> {
    NSString *ident;

    @private
    id _delegate;
    NSMutableArray *_parsedData;
    NSString *_key;
    NSData *_rawData;
    NSXMLParser *_dataParser;
    NSString *_previousTag;
    NSString *_currentTag;
}

@property (retain, nonatomic) NSString *ident;

- (id)initWithKeyValuePair:(JHKeyValuePair *)kvPair;
- (id)initWithKey:(NSString *)Key andData:(NSData *)data;
// delegate management. The delegate is not retained.
- (id <JHXMLParserDelegate>)delegate;
- (void)setDelegate:(id <JHXMLParserDelegate>)delegate;
- (BOOL)start;

@end

And the JHXMLParser.m:

#import "JHKeyValuePair.h"
#import "JHXMLParser.h"

@implementation JHXMLParser

@synthesize ident;

- (id)init {
    if ((self = [super init])) {
        ident = [[NSString alloc] init];
    }
    return self;
}

- (id)initWithKeyValuePair:(JHKeyValuePair *)kvPair {
    if ((self = [self init])) {
        _key = [kvPair key];
        _rawData = [kvPair value];
        _dataParser = [[NSXMLParser alloc] initWithData:_rawData];
        _dataParser.delegate = self;
    }
    return self;
}

- (id)initWithKey:(NSString *)key andData:(NSData *)data {
    if ((self = [self init])) {
        _key = key;
        _rawData = data;
        _dataParser = [[NSXMLParser alloc] initWithData:_rawData];
        _dataParser.delegate = self;
    }
    return self;
}

- (id <JHXMLParserDelegate>)delegate {
    id <JHXMLParserDelegate> d = nil;
    if (_delegate) {
        d = _delegate;
    }
    return d;
}
- (void)setDelegate:(id <JHXMLParserDelegate>)delegate {
    _delegate = delegate;
}

- (BOOL)start {
    return [_dataParser parse];
}

- (void)dealloc {
    [_parsedData release];
    [_rawData release];
    [super dealloc];
}

#pragma mark - NSXMLParser Delegate

- (void)parserDidStartDocument:(NSXMLParser *)parser {
    _parsedData = [[NSMutableArray alloc] init];
}

- (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qualifiedName attributes:(NSDictionary *)attributeDict {
    _currentTag = elementName;
    if ([elementName isEqualToString:_key]) {
        NSMutableDictionary *tmpDict = [[NSMutableDictionary alloc] init];
        [_parsedData addObject:tmpDict];
        [tmpDict release];
    }
}

- (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string {
    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
    if (![_previousTag isEqualToString:_currentTag]) {
        [[_parsedData lastObject] setObject:[string stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]]
                                     forKey:_currentTag];
    } else {
        [[_parsedData lastObject] setObject:[NSString stringWithFormat:@"%@%@",[[_parsedData lastObject] objectForKey:_currentTag], [string stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]]] 
                                     forKey:_currentTag];
    }
    if (_previousTag) {
        [_previousTag release];
        _previousTag = nil;
    }
    _previousTag = [[NSString alloc] initWithFormat:@"%@", _currentTag];
    [pool drain];
}

- (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName {
    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
    if (![_previousTag isEqualToString:elementName]) {
        [[_parsedData lastObject] setObject:@"" forKey:elementName];
    } else {
        if (_previousTag) {
            [_previousTag release];
            _previousTag = nil;
        }
        _previousTag = [[NSString alloc] initWithFormat:@""];
    }
    [pool drain];
}

- (void)parserDidEndDocument:(NSXMLParser *)parser {
    if ([_delegate respondsToSelector:@selector(acceptParsedData:withIdent:)]) {
        [_delegate acceptParsedData:_parsedData withIdent:ident];
    } else if ([_delegate respondsToSelector:@selector(acceptParsedDebugData:withIdent:)]) {
        [_delegate acceptParsedDebugData:_parsedData withIdent:ident];
    }
}

- (void)parser:(NSXMLParser *)parser parseErrorOccurred:(NSError *)parseError {
#ifdef DEBUG
    DEBUGLOG(@"Parser ERROR Occurred: %@", self.ident);
    DEBUGLOG(@"ERROR: %@", parseError);
#endif
    if ([_delegate respondsToSelector:@selector(acceptParsedData:withIdent:)]) {
        [_delegate acceptParsedData:_parsedData withIdent:ident];
    } else if ([_delegate respondsToSelector:@selector(acceptParsedDebugData:withIdent:)]) {
        [_delegate acceptParsedDebugData:_parsedData withIdent:ident];
    }
}

@end

Used like so:

parser = [[[JHXMLParser alloc] initWithKey:@"INFO" andData:connectData] autorelease];
parser.ident = @"INFO";
parser.delegate = self;
[parser start];

Then implement the delegate method:

- (void)acceptParsedData:(NSMutableArray *)parsedData withIdent:(NSString *)ident {
    // do stuff here with the parsed data
}