123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370 |
- /*
- Copyright (c) 2010, Stig Brautaset.
- All rights reserved.
- Redistribution and use in source and binary forms, with or without
- modification, are permitted provided that the following conditions are
- met:
- Redistributions of source code must retain the above copyright
- notice, this list of conditions and the following disclaimer.
- Redistributions in binary form must reproduce the above copyright
- notice, this list of conditions and the following disclaimer in the
- documentation and/or other materials provided with the distribution.
- Neither the name of the the author nor the names of its contributors
- may be used to endorse or promote products derived from this software
- without specific prior written permission.
- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
- IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
- TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
- PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
- #import "SBJsonStreamWriter.h"
- #import "SBJsonStreamWriterState.h"
- static NSNumber *kNotANumber;
- static NSNumber *kTrue;
- static NSNumber *kFalse;
- static NSNumber *kPositiveInfinity;
- static NSNumber *kNegativeInfinity;
- @implementation SBJsonStreamWriter
- @synthesize error;
- @synthesize maxDepth;
- @synthesize state;
- @synthesize stateStack;
- @synthesize humanReadable;
- @synthesize sortKeys;
- @synthesize sortKeysComparator;
- + (void)initialize {
- kNotANumber = [NSDecimalNumber notANumber];
- kPositiveInfinity = [NSNumber numberWithDouble:+INFINITY];
- kNegativeInfinity = [NSNumber numberWithDouble:-INFINITY];
- kTrue = [NSNumber numberWithBool:YES];
- kFalse = [NSNumber numberWithBool:NO];
- }
- #pragma mark Housekeeping
- @synthesize delegate;
- - (id)init {
- self = [super init];
- if (self) {
- maxDepth = 32u;
- stateStack = [[NSMutableArray alloc] initWithCapacity:maxDepth];
- state = [SBJsonStreamWriterStateStart sharedInstance];
- cache = [[NSMutableDictionary alloc] initWithCapacity:32];
- }
- return self;
- }
- #pragma mark Methods
- - (void)appendBytes:(const void *)bytes length:(NSUInteger)length {
- [delegate writer:self appendBytes:bytes length:length];
- }
- - (BOOL)writeObject:(NSDictionary *)dict {
- if (![self writeObjectOpen])
- return NO;
- NSArray *keys = [dict allKeys];
-
- if (sortKeys) {
- if (sortKeysComparator) {
- keys = [keys sortedArrayWithOptions:NSSortStable usingComparator:sortKeysComparator];
- }
- else{
- keys = [keys sortedArrayUsingSelector:@selector(compare:)];
- }
- }
- for (id k in keys) {
- if (![k isKindOfClass:[NSString class]]) {
- self.error = [NSString stringWithFormat:@"JSON object key must be string: %@", k];
- return NO;
- }
- if (![self writeString:k])
- return NO;
- if (![self writeValue:[dict objectForKey:k]])
- return NO;
- }
- return [self writeObjectClose];
- }
- - (BOOL)writeArray:(NSArray*)array {
- if (![self writeArrayOpen])
- return NO;
- for (id v in array)
- if (![self writeValue:v])
- return NO;
- return [self writeArrayClose];
- }
- - (BOOL)writeObjectOpen {
- if ([state isInvalidState:self]) return NO;
- if ([state expectingKey:self]) return NO;
- [state appendSeparator:self];
- if (humanReadable && stateStack.count) [state appendWhitespace:self];
- [stateStack addObject:state];
- self.state = [SBJsonStreamWriterStateObjectStart sharedInstance];
- if (maxDepth && stateStack.count > maxDepth) {
- self.error = @"Nested too deep";
- return NO;
- }
- [delegate writer:self appendBytes:"{" length:1];
- return YES;
- }
- - (BOOL)writeObjectClose {
- if ([state isInvalidState:self]) return NO;
- SBJsonStreamWriterState *prev = state;
- self.state = [stateStack lastObject];
- [stateStack removeLastObject];
- if (humanReadable) [prev appendWhitespace:self];
- [delegate writer:self appendBytes:"}" length:1];
- [state transitionState:self];
- return YES;
- }
- - (BOOL)writeArrayOpen {
- if ([state isInvalidState:self]) return NO;
- if ([state expectingKey:self]) return NO;
- [state appendSeparator:self];
- if (humanReadable && stateStack.count) [state appendWhitespace:self];
- [stateStack addObject:state];
- self.state = [SBJsonStreamWriterStateArrayStart sharedInstance];
- if (maxDepth && stateStack.count > maxDepth) {
- self.error = @"Nested too deep";
- return NO;
- }
- [delegate writer:self appendBytes:"[" length:1];
- return YES;
- }
- - (BOOL)writeArrayClose {
- if ([state isInvalidState:self]) return NO;
- if ([state expectingKey:self]) return NO;
- SBJsonStreamWriterState *prev = state;
- self.state = [stateStack lastObject];
- [stateStack removeLastObject];
- if (humanReadable) [prev appendWhitespace:self];
- [delegate writer:self appendBytes:"]" length:1];
- [state transitionState:self];
- return YES;
- }
- - (BOOL)writeNull {
- if ([state isInvalidState:self]) return NO;
- if ([state expectingKey:self]) return NO;
- [state appendSeparator:self];
- if (humanReadable) [state appendWhitespace:self];
- [delegate writer:self appendBytes:"null" length:4];
- [state transitionState:self];
- return YES;
- }
- - (BOOL)writeBool:(BOOL)x {
- if ([state isInvalidState:self]) return NO;
- if ([state expectingKey:self]) return NO;
- [state appendSeparator:self];
- if (humanReadable) [state appendWhitespace:self];
- if (x)
- [delegate writer:self appendBytes:"true" length:4];
- else
- [delegate writer:self appendBytes:"false" length:5];
- [state transitionState:self];
- return YES;
- }
- - (BOOL)writeValue:(id)o {
- if ([o isKindOfClass:[NSDictionary class]]) {
- return [self writeObject:o];
- } else if ([o isKindOfClass:[NSArray class]]) {
- return [self writeArray:o];
- } else if ([o isKindOfClass:[NSString class]]) {
- [self writeString:o];
- return YES;
- } else if ([o isKindOfClass:[NSNumber class]]) {
- return [self writeNumber:o];
- } else if ([o isKindOfClass:[NSNull class]]) {
- return [self writeNull];
- } else if ([o respondsToSelector:@selector(proxyForJson)]) {
- return [self writeValue:[o proxyForJson]];
- }
- self.error = [NSString stringWithFormat:@"JSON serialisation not supported for %@", [o class]];
- return NO;
- }
- static const char *strForChar(int c) {
- switch (c) {
- case 0: return "\\u0000"; break;
- case 1: return "\\u0001"; break;
- case 2: return "\\u0002"; break;
- case 3: return "\\u0003"; break;
- case 4: return "\\u0004"; break;
- case 5: return "\\u0005"; break;
- case 6: return "\\u0006"; break;
- case 7: return "\\u0007"; break;
- case 8: return "\\b"; break;
- case 9: return "\\t"; break;
- case 10: return "\\n"; break;
- case 11: return "\\u000b"; break;
- case 12: return "\\f"; break;
- case 13: return "\\r"; break;
- case 14: return "\\u000e"; break;
- case 15: return "\\u000f"; break;
- case 16: return "\\u0010"; break;
- case 17: return "\\u0011"; break;
- case 18: return "\\u0012"; break;
- case 19: return "\\u0013"; break;
- case 20: return "\\u0014"; break;
- case 21: return "\\u0015"; break;
- case 22: return "\\u0016"; break;
- case 23: return "\\u0017"; break;
- case 24: return "\\u0018"; break;
- case 25: return "\\u0019"; break;
- case 26: return "\\u001a"; break;
- case 27: return "\\u001b"; break;
- case 28: return "\\u001c"; break;
- case 29: return "\\u001d"; break;
- case 30: return "\\u001e"; break;
- case 31: return "\\u001f"; break;
- case 34: return "\\\""; break;
- case 92: return "\\\\"; break;
- }
- NSLog(@"FUTFUTFUT: -->'%c'<---", c);
- return "FUTFUTFUT";
- }
- - (BOOL)writeString:(NSString*)string {
- if ([state isInvalidState:self]) return NO;
- [state appendSeparator:self];
- if (humanReadable) [state appendWhitespace:self];
- NSMutableData *buf = [cache objectForKey:string];
- if (!buf) {
- NSUInteger len = [string lengthOfBytesUsingEncoding:NSUTF8StringEncoding];
- const char *utf8 = [string UTF8String];
- NSUInteger written = 0, i = 0;
- buf = [NSMutableData dataWithCapacity:(NSUInteger)(len * 1.1f)];
- [buf appendBytes:"\"" length:1];
- for (i = 0; i < len; i++) {
- int c = utf8[i];
- BOOL isControlChar = c >= 0 && c < 32;
- if (isControlChar || c == '"' || c == '\\') {
- if (i - written)
- [buf appendBytes:utf8 + written length:i - written];
- written = i + 1;
- const char *t = strForChar(c);
- [buf appendBytes:t length:strlen(t)];
- }
- }
- if (i - written)
- [buf appendBytes:utf8 + written length:i - written];
- [buf appendBytes:"\"" length:1];
- [cache setObject:buf forKey:string];
- }
- [delegate writer:self appendBytes:[buf bytes] length:[buf length]];
- [state transitionState:self];
- return YES;
- }
- - (BOOL)writeNumber:(NSNumber*)number {
- if (number == kTrue || number == kFalse)
- return [self writeBool:[number boolValue]];
- if ([state isInvalidState:self]) return NO;
- if ([state expectingKey:self]) return NO;
- [state appendSeparator:self];
- if (humanReadable) [state appendWhitespace:self];
- if ([kPositiveInfinity isEqualToNumber:number]) {
- self.error = @"+Infinity is not a valid number in JSON";
- return NO;
- } else if ([kNegativeInfinity isEqualToNumber:number]) {
- self.error = @"-Infinity is not a valid number in JSON";
- return NO;
- } else if ([kNotANumber isEqualToNumber:number]) {
- self.error = @"NaN is not a valid number in JSON";
- return NO;
- }
- const char *objcType = [number objCType];
- char num[128];
- size_t len;
- switch (objcType[0]) {
- case 'c': case 'i': case 's': case 'l': case 'q':
- len = snprintf(num, sizeof num, "%lld", [number longLongValue]);
- break;
- case 'C': case 'I': case 'S': case 'L': case 'Q':
- len = snprintf(num, sizeof num, "%llu", [number unsignedLongLongValue]);
- break;
- case 'f': case 'd': default:
- if ([number isKindOfClass:[NSDecimalNumber class]]) {
- char const *utf8 = [[number stringValue] UTF8String];
- [delegate writer:self appendBytes:utf8 length: strlen(utf8)];
- [state transitionState:self];
- return YES;
- }
- len = snprintf(num, sizeof num, "%.17g", [number doubleValue]);
- break;
- }
- [delegate writer:self appendBytes:num length: len];
- [state transitionState:self];
- return YES;
- }
- @end
|