Created
March 29, 2010 23:14
-
-
Save mrjjwright/348535 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// YAJLTopLevelParser.m | |
// | |
// Created by John Wright on 03/29/10. | |
// Copyright 2010. All rights reserved. | |
// | |
// Permission is hereby granted, free of charge, to any person | |
// obtaining a copy of this software and associated documentation | |
// files (the "Software"), to deal in the Software without | |
// restriction, including without limitation the rights to use, | |
// copy, modify, merge, publish, distribute, sublicense, and/or sell | |
// copies of the Software, and to permit persons to whom the | |
// Software is furnished to do so, subject to the following | |
// conditions: | |
// | |
// The above copyright notice and this permission notice shall be | |
// included in all copies or substantial portions of the Software. | |
// | |
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, | |
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES | |
// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND | |
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT | |
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, | |
// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING | |
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR | |
// OTHER DEALINGS IN THE SOFTWARE. | |
// | |
#import "YAJLTopLevelParser.h" | |
@interface YAJLTopLevelParser () | |
- (void)_pop; | |
- (void)_popKey; | |
@end | |
NSInteger YAJLTopLevelParserStackCapacity = 20; | |
@implementation YAJLTopLevelParser | |
@synthesize parserStatus=parserStatus_, delegate=delegate_; | |
- (id)init { | |
return [self initWithParserOptions:0]; | |
} | |
- (id)initWithParserOptions:(YAJLParserOptions)parserOptions { | |
if ((self = [super init])) { | |
stack_ = [[NSMutableArray alloc] initWithCapacity:YAJLTopLevelParserStackCapacity]; | |
keyStack_ = [[NSMutableArray alloc] initWithCapacity:YAJLTopLevelParserStackCapacity]; | |
parserStatus_ = YAJLParserStatusNone; | |
parser_ = [[YAJLParser alloc] initWithParserOptions:parserOptions]; | |
parser_.delegate = self; | |
firstChunkReceived_ = YES; | |
} | |
return self; | |
} | |
- (id)initWithData:(NSData *)data parserOptions:(YAJLParserOptions)parserOptions error:(NSError **)error { | |
if ((self = [self initWithParserOptions:parserOptions])) { | |
[self parse:data error:error]; | |
} | |
return self; | |
} | |
- (void)dealloc { | |
[stack_ release]; | |
[keyStack_ release]; | |
parser_.delegate = nil; | |
[parser_ release]; | |
[super dealloc]; | |
} | |
- (YAJLParserStatus)parse:(NSData *)data error:(NSError **)error { | |
if (firstChunkReceived_ && [delegate_ respondsToSelector:@selector(parserWillStart:)]) { | |
[delegate_ parserWillStart:self]; | |
} | |
parserStatus_ = [parser_ parse:data]; | |
if (error) *error = [parser_ parserError]; | |
if (*error) { | |
NSLog(@"%@", *error); | |
} | |
if (parserStatus_ == YAJLParserStatusOK && [delegate_ respondsToSelector:@selector(parserDidEnd:)]) { | |
[delegate_ parserDidEnd:self]; | |
} | |
firstChunkReceived_ = NO; | |
return parserStatus_; | |
} | |
#pragma mark Delegates | |
- (void)parser:(YAJLParser *)parser didAdd:(id)value { | |
switch(currentType_) { | |
case YAJLDecoderCurrentTypeArray: | |
[array_ addObject:value]; | |
break; | |
case YAJLDecoderCurrentTypeDict: | |
NSParameterAssert(key_); | |
[dict_ setObject:value forKey:key_]; | |
[self _popKey]; | |
break; | |
} | |
} | |
- (void)parser:(YAJLParser *)parser didMapKey:(NSString *)key { | |
key_ = key; | |
[keyStack_ addObject:key_]; // Push | |
} | |
- (void)_popKey { | |
key_ = nil; | |
[keyStack_ removeLastObject]; // Pop | |
if ([keyStack_ count] > 0) | |
key_ = [keyStaack_ objectAtIndex:[keyStack_ count]-1]; | |
} | |
- (void)parserDidStartDictionary:(YAJLParser *)parser { | |
NSMutableDictionary *dict = [[NSMutableDictionary alloc] initWithCapacity:YAJLTopLevelParserStackCapacity]; | |
[stack_ addObject:dict]; // Push | |
dict_ = dict; | |
[dict release]; | |
currentType_ = YAJLDecoderCurrentTypeDict; | |
} | |
- (void)parserDidEndDictionary:(YAJLParser *)parser { | |
id value = [[stack_ objectAtIndex:[stack_ count]-1] retain]; | |
NSDictionary *dict = dict_; | |
[self _pop]; | |
[self parser:parser didAdd:value]; | |
//our delegate is only interested in top level dictionaries | |
if ([delegate_ respondsToSelector:@selector(parser:didAddTopLevelDictionary:)] && ([stack_ count] <= 1)) { | |
[dict autorelease]; | |
[delegate_ parser:self didAddTopLevelDictionary:dict]; | |
} else { | |
[value release]; | |
} | |
} | |
- (void)parserDidStartArray:(YAJLParser *)parser { | |
//we want to start the stack when we see the first dictionary | |
//of course this ignore json like [1, 2, 4]; | |
//but this parser is for streaming top level dictionaries | |
if (dict_) { | |
NSMutableArray *array = [[NSMutableArray alloc] initWithCapacity:YAJLTopLevelParserStackCapacity]; | |
[stack_ addObject:array]; // Push | |
[array release]; | |
array_ = array; | |
currentType_ = YAJLDecoderCurrentTypeArray; | |
} | |
} | |
- (void)parserDidEndArray:(YAJLParser *)parser { | |
if (dict_) { | |
id value = [[stack_ objectAtIndex:[stack_ count]-1] retain]; | |
[self _pop]; | |
[self parser:parser didAdd:value]; | |
[value release]; | |
} | |
} | |
- (void)_pop { | |
[stack_ removeLastObject]; | |
array_ = nil; | |
dict_ = nil; | |
currentType_ = YAJLDecoderCurrentTypeNone; | |
id value = nil; | |
if ([stack_ count] > 0) value = [stack_ objectAtIndex:[stack_ count]-1]; | |
if ([value isKindOfClass:[NSArray class]]) { | |
array_ = (NSMutableArray *)value; | |
currentType_ = YAJLDecoderCurrentTypeArray; | |
} else if ([value isKindOfClass:[NSDictionary class]]) { | |
dict_ = (NSMutableDictionary *)value; | |
currentType_ = YAJLDecoderCurrentTypeDict; | |
} | |
} | |
@end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// YAJLTopLevelParser.h | |
// | |
// Created by John Wright on 03/29/10. | |
// Copyright 2010. All rights reserved. | |
// | |
// Permission is hereby granted, free of charge, to any person | |
// obtaining a copy of this software and associated documentation | |
// files (the "Software"), to deal in the Software without | |
// restriction, including without limitation the rights to use, | |
// copy, modify, merge, publish, distribute, sublicense, and/or sell | |
// copies of the Software, and to permit persons to whom the | |
// Software is furnished to do so, subject to the following | |
// conditions: | |
// | |
// The above copyright notice and this permission notice shall be | |
// included in all copies or substantial portions of the Software. | |
// | |
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, | |
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES | |
// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND | |
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT | |
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, | |
// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING | |
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR | |
// OTHER DEALINGS IN THE SOFTWARE. | |
// | |
#import <Foundation/Foundation.h> | |
#import "YAJLParser.h" | |
typedef enum { | |
YAJLDecoderCurrentTypeNone, | |
YAJLDecoderCurrentTypeArray, | |
YAJLDecoderCurrentTypeDict | |
} YAJLDecoderCurrentType; | |
extern NSInteger YAJLTopLevelParserStackCapacity; | |
@class YAJLTopLevelParser; | |
@protocol YAJLTopLevelParserDelegate <NSObject> | |
@optional | |
/* Notifies the delegate that the parser is about to parse the | |
* the first chunk of JSON data. | |
* Often a good time to open any resources for streaming, | |
* such as a database handle | |
*/ | |
- (void)parserWillStart:(YAJLTopLevelParser *)parser; | |
/* Pushes to the delegate a complete parsed top-level dictionary. | |
* These are dictionaries located at the highest JSON Level or within a single | |
* enclosing Array. | |
* | |
* This is a convenience function on top of the streaming json parser. | |
* Usually the top level dictionaries are the chunks the delegate | |
* prefers to work with. They represent twitter entries or other API objects, | |
* or perhaps database records. If you need access to lower level | |
* JSON parsing notifications than set the property lowLevelDelegate. | |
*/ | |
- (void)parser:(YAJLTopLevelParser *)parser didAddTopLevelDictionary:(NSDictionary *)dict; | |
/* Notifies the delegate that the parser completed parsing the last | |
* chunk of JSON data successfully. | |
* Often a good time to close or commit any resouces | |
*/ | |
- (void)parserDidEnd:(YAJLTopLevelParser *)parser; | |
@end | |
/* Will stream complete top level Dictionaries to the delegate | |
* releasing them after pushing them to the delegate. | |
* This creates an efficient way to process one top level JSON object | |
* (such as a Twitter status) at a time. | |
*/ | |
@interface YAJLTopLevelParser : NSObject <YAJLParserDelegate> { | |
YAJLParser *parser_; | |
__weak id<YAJLTopLevelParserDelegate> delegate_; | |
__weak NSMutableDictionary *dict_; // weak; if map in progress, points to the current map | |
__weak NSMutableArray *array_; // weak; If array in progress, points the current array | |
__weak NSString *key_; // weak; If map in progress, points to current key | |
NSMutableArray *stack_; | |
NSMutableArray *keyStack_; | |
YAJLDecoderCurrentType currentType_; | |
YAJLParserStatus parserStatus_; | |
BOOL firstChunkReceived_; | |
} | |
@property (readonly, nonatomic) YAJLParserStatus parserStatus; | |
@property (assign, nonatomic) id<YAJLTopLevelParserDelegate> delegate; | |
/*! | |
Create document from data. | |
@param data Data to parse | |
@param parserOptions Parse options | |
@param error Error to set on failure | |
*/ | |
- (id)initWithData:(NSData *)data parserOptions:(YAJLParserOptions)parserOptions error:(NSError **)error; | |
/*! | |
Create empty document with parser options. | |
@param parserOptions Parse options | |
*/ | |
- (id)initWithParserOptions:(YAJLParserOptions)parserOptions; | |
/*! | |
Parse data. | |
@param data Data to parse | |
@param error Error to set on failure | |
@result Parser status | |
*/ | |
- (YAJLParserStatus)parse:(NSData *)data error:(NSError **)error; | |
@end | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment