Skip to content

Instantly share code, notes, and snippets.

@mrjjwright
Created March 29, 2010 23:14
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save mrjjwright/348535 to your computer and use it in GitHub Desktop.
Save mrjjwright/348535 to your computer and use it in GitHub Desktop.
//
// 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
//
// 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