summaryrefslogtreecommitdiffstats
path: root/Classes/Source/JSON
diff options
context:
space:
mode:
Diffstat (limited to 'Classes/Source/JSON')
-rw-r--r--Classes/Source/JSON/CJSONDeserializer.h63
-rw-r--r--Classes/Source/JSON/CJSONDeserializer.m161
-rw-r--r--Classes/Source/JSON/CJSONScanner.h95
-rw-r--r--Classes/Source/JSON/CJSONScanner.m676
-rw-r--r--Classes/Source/JSON/CJSONSerializer.h53
-rw-r--r--Classes/Source/JSON/CJSONSerializer.m342
-rw-r--r--Classes/Source/JSON/JSONRepresentation.h18
7 files changed, 1408 insertions, 0 deletions
diff --git a/Classes/Source/JSON/CJSONDeserializer.h b/Classes/Source/JSON/CJSONDeserializer.h
new file mode 100644
index 0000000..0c3ed02
--- /dev/null
+++ b/Classes/Source/JSON/CJSONDeserializer.h
@@ -0,0 +1,63 @@
+//
+// CJSONDeserializer.h
+// TouchCode
+//
+// Created by Jonathan Wight on 12/15/2005.
+// Copyright 2005 toxicsoftware.com. 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 "CJSONScanner.h"
+
+extern NSString *const kJSONDeserializerErrorDomain /* = @"CJSONDeserializerErrorDomain" */;
+
+enum {
+ kJSONDeserializationOptions_MutableContainers = kJSONScannerOptions_MutableContainers,
+ kJSONDeserializationOptions_MutableLeaves = kJSONScannerOptions_MutableLeaves,
+};
+typedef NSUInteger EJSONDeserializationOptions;
+
+@class CJSONScanner;
+
+@interface CJSONDeserializer : NSObject {
+ CJSONScanner *scanner;
+ EJSONDeserializationOptions options;
+}
+
+@property (readwrite, nonatomic, retain) CJSONScanner *scanner;
+/// Object to return instead when a null encountered in the JSON. Defaults to NSNull. Setting to null causes the scanner to skip null values.
+@property (readwrite, nonatomic, retain) id nullObject;
+/// JSON must be encoded in Unicode (UTF-8, UTF-16 or UTF-32). Use this if you expect to get the JSON in another encoding.
+@property (readwrite, nonatomic, assign) NSStringEncoding allowedEncoding;
+@property (readwrite, nonatomic, assign) EJSONDeserializationOptions options;
+
++ (id)deserializer;
+
+- (id)deserialize:(NSData *)inData error:(NSError **)outError;
+
+- (id)deserializeAsDictionary:(NSData *)inData error:(NSError **)outError;
+- (id)deserializeAsArray:(NSData *)inData error:(NSError **)outError;
+
+@end
diff --git a/Classes/Source/JSON/CJSONDeserializer.m b/Classes/Source/JSON/CJSONDeserializer.m
new file mode 100644
index 0000000..27a2d03
--- /dev/null
+++ b/Classes/Source/JSON/CJSONDeserializer.m
@@ -0,0 +1,161 @@
+//
+// CJSONDeserializer.m
+// TouchCode
+//
+// Created by Jonathan Wight on 12/15/2005.
+// Copyright 2005 toxicsoftware.com. 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 "CJSONDeserializer.h"
+
+#import "CJSONScanner.h"
+#import "CDataScanner.h"
+
+NSString *const kJSONDeserializerErrorDomain = @"CJSONDeserializerErrorDomain";
+
+@interface CJSONDeserializer ()
+@end
+
+@implementation CJSONDeserializer
+
+@synthesize scanner;
+@synthesize options;
+
++ (id)deserializer
+ {
+ return([[[self alloc] init] autorelease]);
+ }
+
+- (id)init
+ {
+ if ((self = [super init]) != NULL)
+ {
+ }
+ return(self);
+ }
+
+- (void)dealloc
+ {
+ [scanner release];
+ scanner = NULL;
+ //
+ [super dealloc];
+ }
+
+#pragma mark -
+
+- (CJSONScanner *)scanner
+ {
+ if (scanner == NULL)
+ {
+ scanner = [[CJSONScanner alloc] init];
+ }
+ return(scanner);
+ }
+
+- (id)nullObject
+ {
+ return(self.scanner.nullObject);
+ }
+
+- (void)setNullObject:(id)inNullObject
+ {
+ self.scanner.nullObject = inNullObject;
+ }
+
+#pragma mark -
+
+- (NSStringEncoding)allowedEncoding
+ {
+ return(self.scanner.allowedEncoding);
+ }
+
+- (void)setAllowedEncoding:(NSStringEncoding)inAllowedEncoding
+ {
+ self.scanner.allowedEncoding = inAllowedEncoding;
+ }
+
+#pragma mark -
+
+- (id)deserialize:(NSData *)inData error:(NSError **)outError
+ {
+ if (inData == NULL || [inData length] == 0)
+ {
+ if (outError)
+ *outError = [NSError errorWithDomain:kJSONDeserializerErrorDomain code:kJSONScannerErrorCode_NothingToScan userInfo:NULL];
+
+ return(NULL);
+ }
+ if ([self.scanner setData:inData error:outError] == NO)
+ {
+ return(NULL);
+ }
+ id theObject = NULL;
+ if ([self.scanner scanJSONObject:&theObject error:outError] == YES)
+ return(theObject);
+ else
+ return(NULL);
+ }
+
+- (id)deserializeAsDictionary:(NSData *)inData error:(NSError **)outError
+ {
+ if (inData == NULL || [inData length] == 0)
+ {
+ if (outError)
+ *outError = [NSError errorWithDomain:kJSONDeserializerErrorDomain code:kJSONScannerErrorCode_NothingToScan userInfo:NULL];
+
+ return(NULL);
+ }
+ if ([self.scanner setData:inData error:outError] == NO)
+ {
+ return(NULL);
+ }
+ NSDictionary *theDictionary = NULL;
+ if ([self.scanner scanJSONDictionary:&theDictionary error:outError] == YES)
+ return(theDictionary);
+ else
+ return(NULL);
+ }
+
+- (id)deserializeAsArray:(NSData *)inData error:(NSError **)outError
+ {
+ if (inData == NULL || [inData length] == 0)
+ {
+ if (outError)
+ *outError = [NSError errorWithDomain:kJSONDeserializerErrorDomain code:kJSONScannerErrorCode_NothingToScan userInfo:NULL];
+
+ return(NULL);
+ }
+ if ([self.scanner setData:inData error:outError] == NO)
+ {
+ return(NULL);
+ }
+ NSArray *theArray = NULL;
+ if ([self.scanner scanJSONArray:&theArray error:outError] == YES)
+ return(theArray);
+ else
+ return(NULL);
+ }
+
+@end
diff --git a/Classes/Source/JSON/CJSONScanner.h b/Classes/Source/JSON/CJSONScanner.h
new file mode 100644
index 0000000..d410893
--- /dev/null
+++ b/Classes/Source/JSON/CJSONScanner.h
@@ -0,0 +1,95 @@
+//
+// CJSONScanner.h
+// TouchCode
+//
+// Created by Jonathan Wight on 12/07/2005.
+// Copyright 2005 toxicsoftware.com. 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 "CDataScanner.h"
+
+enum {
+ kJSONScannerOptions_MutableContainers = 0x1,
+ kJSONScannerOptions_MutableLeaves = 0x2,
+};
+typedef NSUInteger EJSONScannerOptions;
+
+/// CDataScanner subclass that understands JSON syntax natively. You should generally use CJSONDeserializer instead of this class. (TODO - this could have been a category?)
+@interface CJSONScanner : CDataScanner {
+ BOOL strictEscapeCodes;
+ id nullObject;
+ NSStringEncoding allowedEncoding;
+ EJSONScannerOptions options;
+}
+
+@property (readwrite, nonatomic, assign) BOOL strictEscapeCodes;
+@property (readwrite, nonatomic, retain) id nullObject;
+@property (readwrite, nonatomic, assign) NSStringEncoding allowedEncoding;
+@property (readwrite, nonatomic, assign) EJSONScannerOptions options;
+
+- (BOOL)setData:(NSData *)inData error:(NSError **)outError;
+
+- (BOOL)scanJSONObject:(id *)outObject error:(NSError **)outError;
+- (BOOL)scanJSONDictionary:(NSDictionary **)outDictionary error:(NSError **)outError;
+- (BOOL)scanJSONArray:(NSArray **)outArray error:(NSError **)outError;
+- (BOOL)scanJSONStringConstant:(NSString **)outStringConstant error:(NSError **)outError;
+- (BOOL)scanJSONNumberConstant:(NSNumber **)outNumberConstant error:(NSError **)outError;
+
+@end
+
+extern NSString *const kJSONScannerErrorDomain /* = @"kJSONScannerErrorDomain" */;
+
+typedef enum {
+
+ // Fundamental scanning errors
+ kJSONScannerErrorCode_NothingToScan = -11,
+ kJSONScannerErrorCode_CouldNotDecodeData = -12,
+ kJSONScannerErrorCode_CouldNotSerializeData = -13,
+ kJSONScannerErrorCode_CouldNotSerializeObject = -14,
+ kJSONScannerErrorCode_CouldNotScanObject = -15,
+
+ // Dictionary scanning
+ kJSONScannerErrorCode_DictionaryStartCharacterMissing = -101,
+ kJSONScannerErrorCode_DictionaryKeyScanFailed = -102,
+ kJSONScannerErrorCode_DictionaryKeyNotTerminated = -103,
+ kJSONScannerErrorCode_DictionaryValueScanFailed = -104,
+ kJSONScannerErrorCode_DictionaryKeyValuePairNoDelimiter = -105,
+ kJSONScannerErrorCode_DictionaryNotTerminated = -106,
+
+ // Array scanning
+ kJSONScannerErrorCode_ArrayStartCharacterMissing = -201,
+ kJSONScannerErrorCode_ArrayValueScanFailed = -202,
+ kJSONScannerErrorCode_ArrayValueIsNull = -203,
+ kJSONScannerErrorCode_ArrayNotTerminated = -204,
+
+ // String scanning
+ kJSONScannerErrorCode_StringNotStartedWithBackslash = -301,
+ kJSONScannerErrorCode_StringUnicodeNotDecoded = -302,
+ kJSONScannerErrorCode_StringUnknownEscapeCode = -303,
+ kJSONScannerErrorCode_StringNotTerminated = -304,
+
+ // Number scanning
+ kJSONScannerErrorCode_NumberNotScannable = -401
+
+} EJSONScannerErrorCode;
diff --git a/Classes/Source/JSON/CJSONScanner.m b/Classes/Source/JSON/CJSONScanner.m
new file mode 100644
index 0000000..c5ffeb4
--- /dev/null
+++ b/Classes/Source/JSON/CJSONScanner.m
@@ -0,0 +1,676 @@
+//
+// CJSONScanner.m
+// TouchCode
+//
+// Created by Jonathan Wight on 12/07/2005.
+// Copyright 2005 toxicsoftware.com. 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 "CJSONScanner.h"
+
+#import "CDataScanner_Extensions.h"
+
+#if !defined(TREAT_COMMENTS_AS_WHITESPACE)
+#define TREAT_COMMENTS_AS_WHITESPACE 0
+#endif // !defined(TREAT_COMMENTS_AS_WHITESPACE)
+
+NSString *const kJSONScannerErrorDomain = @"kJSONScannerErrorDomain";
+
+inline static int HexToInt(char inCharacter)
+ {
+ int theValues[] = { 0x0 /* 48 '0' */, 0x1 /* 49 '1' */, 0x2 /* 50 '2' */, 0x3 /* 51 '3' */, 0x4 /* 52 '4' */, 0x5 /* 53 '5' */, 0x6 /* 54 '6' */, 0x7 /* 55 '7' */, 0x8 /* 56 '8' */, 0x9 /* 57 '9' */, -1 /* 58 ':' */, -1 /* 59 ';' */, -1 /* 60 '<' */, -1 /* 61 '=' */, -1 /* 62 '>' */, -1 /* 63 '?' */, -1 /* 64 '@' */, 0xa /* 65 'A' */, 0xb /* 66 'B' */, 0xc /* 67 'C' */, 0xd /* 68 'D' */, 0xe /* 69 'E' */, 0xf /* 70 'F' */, -1 /* 71 'G' */, -1 /* 72 'H' */, -1 /* 73 'I' */, -1 /* 74 'J' */, -1 /* 75 'K' */, -1 /* 76 'L' */, -1 /* 77 'M' */, -1 /* 78 'N' */, -1 /* 79 'O' */, -1 /* 80 'P' */, -1 /* 81 'Q' */, -1 /* 82 'R' */, -1 /* 83 'S' */, -1 /* 84 'T' */, -1 /* 85 'U' */, -1 /* 86 'V' */, -1 /* 87 'W' */, -1 /* 88 'X' */, -1 /* 89 'Y' */, -1 /* 90 'Z' */, -1 /* 91 '[' */, -1 /* 92 '\' */, -1 /* 93 ']' */, -1 /* 94 '^' */, -1 /* 95 '_' */, -1 /* 96 '`' */, 0xa /* 97 'a' */, 0xb /* 98 'b' */, 0xc /* 99 'c' */, 0xd /* 100 'd' */, 0xe /* 101 'e' */, 0xf /* 102 'f' */, };
+ if (inCharacter >= '0' && inCharacter <= 'f')
+ return(theValues[inCharacter - '0']);
+ else
+ return(-1);
+ }
+
+@interface CJSONScanner ()
+- (BOOL)scanNotQuoteCharactersIntoString:(NSString **)outValue;
+@end
+
+#pragma mark -
+
+@implementation CJSONScanner
+
+@synthesize strictEscapeCodes;
+@synthesize nullObject;
+@synthesize allowedEncoding;
+@synthesize options;
+
+- (id)init
+ {
+ if ((self = [super init]) != NULL)
+ {
+ strictEscapeCodes = NO;
+ nullObject = [[NSNull null] retain];
+ }
+ return(self);
+ }
+
+- (void)dealloc
+ {
+ [nullObject release];
+ nullObject = NULL;
+ //
+ [super dealloc];
+ }
+
+#pragma mark -
+
+- (BOOL)setData:(NSData *)inData error:(NSError **)outError;
+ {
+ NSData *theData = inData;
+ if (theData && theData.length >= 4)
+ {
+ // This code is lame, but it works. Because the first character of any JSON string will always be a (ascii) control character we can work out the Unicode encoding by the bit pattern. See section 3 of http://www.ietf.org/rfc/rfc4627.txt
+ const char *theChars = theData.bytes;
+ NSStringEncoding theEncoding = NSUTF8StringEncoding;
+ if (theChars[0] != 0 && theChars[1] == 0)
+ {
+ if (theChars[2] != 0 && theChars[3] == 0)
+ theEncoding = NSUTF16LittleEndianStringEncoding;
+ else if (theChars[2] == 0 && theChars[3] == 0)
+ theEncoding = NSUTF32LittleEndianStringEncoding;
+ }
+ else if (theChars[0] == 0 && theChars[2] == 0 && theChars[3] != 0)
+ {
+ if (theChars[1] == 0)
+ theEncoding = NSUTF32BigEndianStringEncoding;
+ else if (theChars[1] != 0)
+ theEncoding = NSUTF16BigEndianStringEncoding;
+ }
+
+ NSString *theString = [[NSString alloc] initWithData:theData encoding:theEncoding];
+ if (theString == NULL && self.allowedEncoding != 0)
+ {
+ theString = [[NSString alloc] initWithData:theData encoding:self.allowedEncoding];
+ }
+ theData = [theString dataUsingEncoding:NSUTF8StringEncoding];
+ [theString release];
+ }
+
+ if (theData)
+ {
+ [super setData:theData];
+ return(YES);
+ }
+ else
+ {
+ if (outError)
+ {
+ NSMutableDictionary *theUserInfo = [NSMutableDictionary dictionaryWithObjectsAndKeys:
+ @"Could not scan data. Data wasn't encoded properly?", NSLocalizedDescriptionKey,
+ NULL];
+ [theUserInfo addEntriesFromDictionary:self.userInfoForScanLocation];
+ *outError = [NSError errorWithDomain:kJSONScannerErrorDomain code:kJSONScannerErrorCode_CouldNotDecodeData userInfo:theUserInfo];
+ }
+ return(NO);
+ }
+ }
+
+- (void)setData:(NSData *)inData
+ {
+ [self setData:inData error:NULL];
+ }
+
+#pragma mark -
+
+- (BOOL)scanJSONObject:(id *)outObject error:(NSError **)outError
+ {
+ BOOL theResult = YES;
+
+ [self skipWhitespace];
+
+ id theObject = NULL;
+
+ const unichar C = [self currentCharacter];
+ switch (C)
+ {
+ case 't':
+ if ([self scanUTF8String:"true" intoString:NULL])
+ {
+ theObject = [NSNumber numberWithBool:YES];
+ }
+ break;
+ case 'f':
+ if ([self scanUTF8String:"false" intoString:NULL])
+ {
+ theObject = [NSNumber numberWithBool:NO];
+ }
+ break;
+ case 'n':
+ if ([self scanUTF8String:"null" intoString:NULL])
+ {
+ theObject = self.nullObject;
+ }
+ break;
+ case '\"':
+ case '\'':
+ theResult = [self scanJSONStringConstant:&theObject error:outError];
+ break;
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ case '-':
+ theResult = [self scanJSONNumberConstant:&theObject error:outError];
+ break;
+ case '{':
+ theResult = [self scanJSONDictionary:&theObject error:outError];
+ break;
+ case '[':
+ theResult = [self scanJSONArray:&theObject error:outError];
+ break;
+ default:
+ theResult = NO;
+ if (outError)
+ {
+ NSMutableDictionary *theUserInfo = [NSMutableDictionary dictionaryWithObjectsAndKeys:
+ @"Could not scan object. Character not a valid JSON character.", NSLocalizedDescriptionKey,
+ NULL];
+ [theUserInfo addEntriesFromDictionary:self.userInfoForScanLocation];
+ *outError = [NSError errorWithDomain:kJSONScannerErrorDomain code:kJSONScannerErrorCode_CouldNotScanObject userInfo:theUserInfo];
+ }
+ break;
+ }
+
+ if (outObject != NULL)
+ *outObject = theObject;
+
+ return(theResult);
+ }
+
+- (BOOL)scanJSONDictionary:(NSDictionary **)outDictionary error:(NSError **)outError
+ {
+ NSUInteger theScanLocation = [self scanLocation];
+
+ [self skipWhitespace];
+
+ if ([self scanCharacter:'{'] == NO)
+ {
+ if (outError)
+ {
+ NSMutableDictionary *theUserInfo = [NSMutableDictionary dictionaryWithObjectsAndKeys:
+ @"Could not scan dictionary. Dictionary that does not start with '{' character.", NSLocalizedDescriptionKey,
+ NULL];
+ [theUserInfo addEntriesFromDictionary:self.userInfoForScanLocation];
+ *outError = [NSError errorWithDomain:kJSONScannerErrorDomain code:kJSONScannerErrorCode_DictionaryStartCharacterMissing userInfo:theUserInfo];
+ }
+ return(NO);
+ }
+
+ NSMutableDictionary *theDictionary = [[NSMutableDictionary alloc] init];
+
+ while ([self currentCharacter] != '}')
+ {
+ [self skipWhitespace];
+
+ if ([self currentCharacter] == '}')
+ break;
+
+ NSString *theKey = NULL;
+ if ([self scanJSONStringConstant:&theKey error:outError] == NO)
+ {
+ [self setScanLocation:theScanLocation];
+ if (outError)
+ {
+ NSMutableDictionary *theUserInfo = [NSMutableDictionary dictionaryWithObjectsAndKeys:
+ @"Could not scan dictionary. Failed to scan a key.", NSLocalizedDescriptionKey,
+ NULL];
+ [theUserInfo addEntriesFromDictionary:self.userInfoForScanLocation];
+ *outError = [NSError errorWithDomain:kJSONScannerErrorDomain code:kJSONScannerErrorCode_DictionaryKeyScanFailed userInfo:theUserInfo];
+ }
+ [theDictionary release];
+ return(NO);
+ }
+
+ [self skipWhitespace];
+
+ if ([self scanCharacter:':'] == NO)
+ {
+ [self setScanLocation:theScanLocation];
+ if (outError)
+ {
+ NSMutableDictionary *theUserInfo = [NSMutableDictionary dictionaryWithObjectsAndKeys:
+ @"Could not scan dictionary. Key was not terminated with a ':' character.", NSLocalizedDescriptionKey,
+ NULL];
+ [theUserInfo addEntriesFromDictionary:self.userInfoForScanLocation];
+ *outError = [NSError errorWithDomain:kJSONScannerErrorDomain code:kJSONScannerErrorCode_DictionaryKeyNotTerminated userInfo:theUserInfo];
+ }
+ [theDictionary release];
+ return(NO);
+ }
+
+ id theValue = NULL;
+ if ([self scanJSONObject:&theValue error:outError] == NO)
+ {
+ [self setScanLocation:theScanLocation];
+ if (outError)
+ {
+ NSMutableDictionary *theUserInfo = [NSMutableDictionary dictionaryWithObjectsAndKeys:
+ @"Could not scan dictionary. Failed to scan a value.", NSLocalizedDescriptionKey,
+ NULL];
+
+ [theUserInfo addEntriesFromDictionary:self.userInfoForScanLocation];
+ *outError = [NSError errorWithDomain:kJSONScannerErrorDomain code:kJSONScannerErrorCode_DictionaryValueScanFailed userInfo:theUserInfo];
+ }
+ [theDictionary release];
+ return(NO);
+ }
+
+ if (theValue == NULL && self.nullObject == NULL)
+ {
+ // If the value is a null and nullObject is also null then we're skipping this key/value pair.
+ }
+ else
+ {
+ [theDictionary setValue:theValue forKey:theKey];
+ }
+
+ [self skipWhitespace];
+ if ([self scanCharacter:','] == NO)
+ {
+ if ([self currentCharacter] != '}')
+ {
+ [self setScanLocation:theScanLocation];
+ if (outError)
+ {
+ NSMutableDictionary *theUserInfo = [NSMutableDictionary dictionaryWithObjectsAndKeys:
+ @"Could not scan dictionary. Key value pairs not delimited with a ',' character.", NSLocalizedDescriptionKey,
+ NULL];
+ [theUserInfo addEntriesFromDictionary:self.userInfoForScanLocation];
+ *outError = [NSError errorWithDomain:kJSONScannerErrorDomain code:kJSONScannerErrorCode_DictionaryKeyValuePairNoDelimiter userInfo:theUserInfo];
+ }
+ [theDictionary release];
+ return(NO);
+ }
+ break;
+ }
+ else
+ {
+ [self skipWhitespace];
+ if ([self currentCharacter] == '}')
+ break;
+ }
+ }
+
+ if ([self scanCharacter:'}'] == NO)
+ {
+ [self setScanLocation:theScanLocation];
+ if (outError)
+ {
+ NSMutableDictionary *theUserInfo = [NSMutableDictionary dictionaryWithObjectsAndKeys:
+ @"Could not scan dictionary. Dictionary not terminated by a '}' character.", NSLocalizedDescriptionKey,
+ NULL];
+ [theUserInfo addEntriesFromDictionary:self.userInfoForScanLocation];
+ *outError = [NSError errorWithDomain:kJSONScannerErrorDomain code:kJSONScannerErrorCode_DictionaryNotTerminated userInfo:theUserInfo];
+ }
+ [theDictionary release];
+ return(NO);
+ }
+
+ if (outDictionary != NULL)
+ {
+ if (self.options & kJSONScannerOptions_MutableContainers)
+ {
+ *outDictionary = [theDictionary autorelease];
+ }
+ else
+ {
+ *outDictionary = [[theDictionary copy] autorelease];
+ [theDictionary release];
+ }
+ }
+ else
+ {
+ [theDictionary release];
+ }
+
+ return(YES);
+ }
+
+- (BOOL)scanJSONArray:(NSArray **)outArray error:(NSError **)outError
+ {
+ NSUInteger theScanLocation = [self scanLocation];
+
+ [self skipWhitespace];
+
+ if ([self scanCharacter:'['] == NO)
+ {
+ if (outError)
+ {
+ NSMutableDictionary *theUserInfo = [NSMutableDictionary dictionaryWithObjectsAndKeys:
+ @"Could not scan array. Array not started by a '[' character.", NSLocalizedDescriptionKey,
+ NULL];
+ [theUserInfo addEntriesFromDictionary:self.userInfoForScanLocation];
+ *outError = [NSError errorWithDomain:kJSONScannerErrorDomain code:kJSONScannerErrorCode_ArrayStartCharacterMissing userInfo:theUserInfo];
+ }
+ return(NO);
+ }
+
+ NSMutableArray *theArray = [[NSMutableArray alloc] init];
+
+ [self skipWhitespace];
+ while ([self currentCharacter] != ']')
+ {
+ NSString *theValue = NULL;
+ if ([self scanJSONObject:&theValue error:outError] == NO)
+ {
+ [self setScanLocation:theScanLocation];
+ if (outError)
+ {
+ NSMutableDictionary *theUserInfo = [NSMutableDictionary dictionaryWithObjectsAndKeys:
+ @"Could not scan array. Could not scan a value.", NSLocalizedDescriptionKey,
+ NULL];
+ [theUserInfo addEntriesFromDictionary:self.userInfoForScanLocation];
+ *outError = [NSError errorWithDomain:kJSONScannerErrorDomain code:kJSONScannerErrorCode_ArrayValueScanFailed userInfo:theUserInfo];
+ }
+ [theArray release];
+ return(NO);
+ }
+
+ if (theValue == NULL)
+ {
+ if (self.nullObject != NULL)
+ {
+ if (outError)
+ {
+ NSMutableDictionary *theUserInfo = [NSMutableDictionary dictionaryWithObjectsAndKeys:
+ @"Could not scan array. Value is NULL.", NSLocalizedDescriptionKey,
+ NULL];
+ [theUserInfo addEntriesFromDictionary:self.userInfoForScanLocation];
+ *outError = [NSError errorWithDomain:kJSONScannerErrorDomain code:kJSONScannerErrorCode_ArrayValueIsNull userInfo:theUserInfo];
+ }
+ [theArray release];
+ return(NO);
+ }
+ }
+ else
+ {
+ [theArray addObject:theValue];
+ }
+
+ [self skipWhitespace];
+ if ([self scanCharacter:','] == NO)
+ {
+ [self skipWhitespace];
+ if ([self currentCharacter] != ']')
+ {
+ [self setScanLocation:theScanLocation];
+ if (outError)
+ {
+ NSMutableDictionary *theUserInfo = [NSMutableDictionary dictionaryWithObjectsAndKeys:
+ @"Could not scan array. Array not terminated by a ']' character.", NSLocalizedDescriptionKey,
+ NULL];
+ [theUserInfo addEntriesFromDictionary:self.userInfoForScanLocation];
+ *outError = [NSError errorWithDomain:kJSONScannerErrorDomain code:kJSONScannerErrorCode_ArrayNotTerminated userInfo:theUserInfo];
+ }
+ [theArray release];
+ return(NO);
+ }
+
+ break;
+ }
+ [self skipWhitespace];
+ }
+
+ [self skipWhitespace];
+
+ if ([self scanCharacter:']'] == NO)
+ {
+ [self setScanLocation:theScanLocation];
+ if (outError)
+ {
+ NSMutableDictionary *theUserInfo = [NSMutableDictionary dictionaryWithObjectsAndKeys:
+ @"Could not scan array. Array not terminated by a ']' character.", NSLocalizedDescriptionKey,
+ NULL];
+ [theUserInfo addEntriesFromDictionary:self.userInfoForScanLocation];
+ *outError = [NSError errorWithDomain:kJSONScannerErrorDomain code:kJSONScannerErrorCode_ArrayNotTerminated userInfo:theUserInfo];
+ }
+ [theArray release];
+ return(NO);
+ }
+
+ if (outArray != NULL)
+ {
+ if (self.options & kJSONScannerOptions_MutableContainers)
+ {
+ *outArray = [theArray autorelease];
+ }
+ else
+ {
+ *outArray = [[theArray copy] autorelease];
+ [theArray release];
+ }
+ }
+ else
+ {
+ [theArray release];
+ }
+ return(YES);
+ }
+
+- (BOOL)scanJSONStringConstant:(NSString **)outStringConstant error:(NSError **)outError
+ {
+ NSUInteger theScanLocation = [self scanLocation];
+
+ [self skipWhitespace];
+
+ NSMutableString *theString = [[NSMutableString alloc] init];
+
+ if ([self scanCharacter:'"'] == NO)
+ {
+ [self setScanLocation:theScanLocation];
+ if (outError)
+ {
+ NSMutableDictionary *theUserInfo = [NSMutableDictionary dictionaryWithObjectsAndKeys:
+ @"Could not scan string constant. String not started by a '\"' character.", NSLocalizedDescriptionKey,
+ NULL];
+ [theUserInfo addEntriesFromDictionary:self.userInfoForScanLocation];
+ *outError = [NSError errorWithDomain:kJSONScannerErrorDomain code:kJSONScannerErrorCode_StringNotStartedWithBackslash userInfo:theUserInfo];
+ }
+ [theString release];
+ return(NO);
+ }
+
+ while ([self scanCharacter:'"'] == NO)
+ {
+ NSString *theStringChunk = NULL;
+ if ([self scanNotQuoteCharactersIntoString:&theStringChunk])
+ {
+ CFStringAppend((CFMutableStringRef)theString, (CFStringRef)theStringChunk);
+ }
+ else if ([self scanCharacter:'\\'] == YES)
+ {
+ unichar theCharacter = [self scanCharacter];
+ switch (theCharacter)
+ {
+ case '"':
+ case '\\':
+ case '/':
+ break;
+ case 'b':
+ theCharacter = '\b';
+ break;
+ case 'f':
+ theCharacter = '\f';
+ break;
+ case 'n':
+ theCharacter = '\n';
+ break;
+ case 'r':
+ theCharacter = '\r';
+ break;
+ case 't':
+ theCharacter = '\t';
+ break;
+ case 'u':
+ {
+ theCharacter = 0;
+
+ int theShift;
+ for (theShift = 12; theShift >= 0; theShift -= 4)
+ {
+ const int theDigit = HexToInt([self scanCharacter]);
+ if (theDigit == -1)
+ {
+ [self setScanLocation:theScanLocation];
+ if (outError)
+ {
+ NSMutableDictionary *theUserInfo = [NSMutableDictionary dictionaryWithObjectsAndKeys:
+ @"Could not scan string constant. Unicode character could not be decoded.", NSLocalizedDescriptionKey,
+ NULL];
+ [theUserInfo addEntriesFromDictionary:self.userInfoForScanLocation];
+ *outError = [NSError errorWithDomain:kJSONScannerErrorDomain code:kJSONScannerErrorCode_StringUnicodeNotDecoded userInfo:theUserInfo];
+ }
+ [theString release];
+ return(NO);
+ }
+ theCharacter |= (theDigit << theShift);
+ }
+ }
+ break;
+ default:
+ {
+ if (strictEscapeCodes == YES)
+ {
+ [self setScanLocation:theScanLocation];
+ if (outError)
+ {
+ NSMutableDictionary *theUserInfo = [NSMutableDictionary dictionaryWithObjectsAndKeys:
+ @"Could not scan string constant. Unknown escape code.", NSLocalizedDescriptionKey,
+ NULL];
+ [theUserInfo addEntriesFromDictionary:self.userInfoForScanLocation];
+ *outError = [NSError errorWithDomain:kJSONScannerErrorDomain code:kJSONScannerErrorCode_StringUnknownEscapeCode userInfo:theUserInfo];
+ }
+ [theString release];
+ return(NO);
+ }
+ }
+ break;
+ }
+ CFStringAppendCharacters((CFMutableStringRef)theString, &theCharacter, 1);
+ }
+ else
+ {
+ if (outError)
+ {
+ NSMutableDictionary *theUserInfo = [NSMutableDictionary dictionaryWithObjectsAndKeys:
+ @"Could not scan string constant. No terminating double quote character.", NSLocalizedDescriptionKey,
+ NULL];
+ [theUserInfo addEntriesFromDictionary:self.userInfoForScanLocation];
+ *outError = [NSError errorWithDomain:kJSONScannerErrorDomain code:kJSONScannerErrorCode_StringNotTerminated userInfo:theUserInfo];
+ }
+ [theString release];
+ return(NO);
+ }
+ }
+
+ if (outStringConstant != NULL)
+ {
+ if (self.options & kJSONScannerOptions_MutableLeaves)
+ {
+ *outStringConstant = [theString autorelease];
+ }
+ else
+ {
+ *outStringConstant = [[theString copy] autorelease];
+ [theString release];
+ }
+ }
+ else
+ {
+ [theString release];
+ }
+
+ return(YES);
+ }
+
+- (BOOL)scanJSONNumberConstant:(NSNumber **)outNumberConstant error:(NSError **)outError
+ {
+ NSNumber *theNumber = NULL;
+
+ [self skipWhitespace];
+
+ if ([self scanNumber:&theNumber] == YES)
+ {
+ if (outNumberConstant != NULL)
+ *outNumberConstant = theNumber;
+ return(YES);
+ }
+ else
+ {
+ if (outError)
+ {
+ NSMutableDictionary *theUserInfo = [NSMutableDictionary dictionaryWithObjectsAndKeys:
+ @"Could not scan number constant.", NSLocalizedDescriptionKey,
+ NULL];
+ [theUserInfo addEntriesFromDictionary:self.userInfoForScanLocation];
+ *outError = [NSError errorWithDomain:kJSONScannerErrorDomain code:kJSONScannerErrorCode_NumberNotScannable userInfo:theUserInfo];
+ }
+ return(NO);
+ }
+ }
+
+#if TREAT_COMMENTS_AS_WHITESPACE
+- (void)skipWhitespace
+ {
+ [super skipWhitespace];
+ [self scanCStyleComment:NULL];
+ [self scanCPlusPlusStyleComment:NULL];
+ [super skipWhitespace];
+ }
+#endif // TREAT_COMMENTS_AS_WHITESPACE
+
+#pragma mark -
+
+- (BOOL)scanNotQuoteCharactersIntoString:(NSString **)outValue
+ {
+ u_int8_t *P;
+ for (P = current; P < end && *P != '\"' && *P != '\\'; ++P)
+ ;
+
+ if (P == current)
+ {
+ return(NO);
+ }
+
+ if (outValue)
+ {
+ *outValue = [[[NSString alloc] initWithBytes:current length:P - current encoding:NSUTF8StringEncoding] autorelease];
+ }
+
+ current = P;
+
+ return(YES);
+ }
+
+@end
diff --git a/Classes/Source/JSON/CJSONSerializer.h b/Classes/Source/JSON/CJSONSerializer.h
new file mode 100644
index 0000000..748a85c
--- /dev/null
+++ b/Classes/Source/JSON/CJSONSerializer.h
@@ -0,0 +1,53 @@
+//
+// CJSONSerializer.h
+// TouchCode
+//
+// Created by Jonathan Wight on 12/07/2005.
+// Copyright 2005 toxicsoftware.com. 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>
+
+@interface CJSONSerializer : NSObject {
+}
+
++ (id)serializer;
+
+- (BOOL)isValidJSONObject:(id)inObject;
+
+/// Take any JSON compatible object (generally NSNull, NSNumber, NSString, NSArray and NSDictionary) and produce an NSData containing the serialized JSON.
+- (NSData *)serializeObject:(id)inObject error:(NSError **)outError;
+
+- (NSData *)serializeNull:(NSNull *)inNull error:(NSError **)outError;
+- (NSData *)serializeNumber:(NSNumber *)inNumber error:(NSError **)outError;
+- (NSData *)serializeString:(NSString *)inString error:(NSError **)outError;
+- (NSData *)serializeArray:(NSArray *)inArray error:(NSError **)outError;
+- (NSData *)serializeDictionary:(NSDictionary *)inDictionary error:(NSError **)outError;
+
+@end
+
+typedef enum {
+ CJSONSerializerErrorCouldNotSerializeDataType = -1,
+ CJSONSerializerErrorCouldNotSerializeObject = -1
+} CJSONSerializerError;
diff --git a/Classes/Source/JSON/CJSONSerializer.m b/Classes/Source/JSON/CJSONSerializer.m
new file mode 100644
index 0000000..952b3c2
--- /dev/null
+++ b/Classes/Source/JSON/CJSONSerializer.m
@@ -0,0 +1,342 @@
+//
+// CJSONSerializer.m
+// TouchCode
+//
+// Created by Jonathan Wight on 12/07/2005.
+// Copyright 2005 toxicsoftware.com. 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 "CJSONSerializer.h"
+
+#import "JSONRepresentation.h"
+
+static NSData *kNULL = NULL;
+static NSData *kFalse = NULL;
+static NSData *kTrue = NULL;
+
+@implementation CJSONSerializer
+
++ (void)initialize
+ {
+ NSAutoreleasePool *thePool = [[NSAutoreleasePool alloc] init];
+
+ if (self == [CJSONSerializer class])
+ {
+ if (kNULL == NULL)
+ kNULL = [[NSData alloc] initWithBytesNoCopy:(void *)"null" length:4 freeWhenDone:NO];
+ if (kFalse == NULL)
+ kFalse = [[NSData alloc] initWithBytesNoCopy:(void *)"false" length:5 freeWhenDone:NO];
+ if (kTrue == NULL)
+ kTrue = [[NSData alloc] initWithBytesNoCopy:(void *)"true" length:4 freeWhenDone:NO];
+
+ [thePool release];
+ }
+ }
+
++ (id)serializer
+ {
+ return([[[self alloc] init] autorelease]);
+ }
+
+- (BOOL)isValidJSONObject:(id)inObject
+ {
+ if ([inObject isKindOfClass:[NSNull class]])
+ {
+ return(YES);
+ }
+ else if ([inObject isKindOfClass:[NSNumber class]])
+ {
+ return(YES);
+ }
+ else if ([inObject isKindOfClass:[NSString class]])
+ {
+ return(YES);
+ }
+ else if ([inObject isKindOfClass:[NSArray class]])
+ {
+ return(YES);
+ }
+ else if ([inObject isKindOfClass:[NSDictionary class]])
+ {
+ return(YES);
+ }
+ else if ([inObject isKindOfClass:[NSData class]])
+ {
+ return(YES);
+ }
+ else if ([inObject respondsToSelector:@selector(JSONDataRepresentation)])
+ {
+ return(YES);
+ }
+ else
+ {
+ return(NO);
+ }
+ }
+
+- (NSData *)serializeObject:(id)inObject error:(NSError **)outError
+ {
+ NSData *theResult = NULL;
+
+ if ([inObject isKindOfClass:[NSNull class]])
+ {
+ theResult = [self serializeNull:inObject error:outError];
+ }
+ else if ([inObject isKindOfClass:[NSNumber class]])
+ {
+ theResult = [self serializeNumber:inObject error:outError];
+ }
+ else if ([inObject isKindOfClass:[NSString class]])
+ {
+ theResult = [self serializeString:inObject error:outError];
+ }
+ else if ([inObject isKindOfClass:[NSArray class]])
+ {
+ theResult = [self serializeArray:inObject error:outError];
+ }
+ else if ([inObject isKindOfClass:[NSDictionary class]])
+ {
+ theResult = [self serializeDictionary:inObject error:outError];
+ }
+ else if ([inObject isKindOfClass:[NSData class]])
+ {
+ NSString *theString = [[[NSString alloc] initWithData:inObject encoding:NSUTF8StringEncoding] autorelease];
+ theResult = [self serializeString:theString error:outError];
+ }
+ else if ([inObject respondsToSelector:@selector(JSONDataRepresentation)])
+ {
+ theResult = [inObject JSONDataRepresentation];
+ }
+ else
+ {
+ if (outError)
+ {
+ NSDictionary *theUserInfo = [NSDictionary dictionaryWithObjectsAndKeys:
+ [NSString stringWithFormat:@"Cannot serialize data of type '%@'", NSStringFromClass([inObject class])], NSLocalizedDescriptionKey,
+ NULL];
+ *outError = [NSError errorWithDomain:@"TODO_DOMAIN" code:CJSONSerializerErrorCouldNotSerializeDataType userInfo:theUserInfo];
+ }
+ return(NULL);
+ }
+ if (theResult == NULL)
+ {
+ if (outError)
+ {
+ NSDictionary *theUserInfo = [NSDictionary dictionaryWithObjectsAndKeys:
+ [NSString stringWithFormat:@"Could not serialize object '%@'", inObject], NSLocalizedDescriptionKey,
+ NULL];
+ *outError = [NSError errorWithDomain:@"TODO_DOMAIN" code:CJSONSerializerErrorCouldNotSerializeObject userInfo:theUserInfo];
+ }
+ return(NULL);
+ }
+ return(theResult);
+ }
+
+- (NSData *)serializeNull:(NSNull *)inNull error:(NSError **)outError
+ {
+ #pragma unused (inNull, outError)
+ return(kNULL);
+ }
+
+- (NSData *)serializeNumber:(NSNumber *)inNumber error:(NSError **)outError
+ {
+ #pragma unused (outError)
+ NSData *theResult = NULL;
+ switch (CFNumberGetType((CFNumberRef)inNumber))
+ {
+ case kCFNumberCharType:
+ {
+ int theValue = [inNumber intValue];
+ if (theValue == 0)
+ theResult = kFalse;
+ else if (theValue == 1)
+ theResult = kTrue;
+ else
+ theResult = [[inNumber stringValue] dataUsingEncoding:NSASCIIStringEncoding];
+ }
+ break;
+ case kCFNumberFloat32Type:
+ case kCFNumberFloat64Type:
+ case kCFNumberFloatType:
+ case kCFNumberDoubleType:
+ case kCFNumberSInt8Type:
+ case kCFNumberSInt16Type:
+ case kCFNumberSInt32Type:
+ case kCFNumberSInt64Type:
+ case kCFNumberShortType:
+ case kCFNumberIntType:
+ case kCFNumberLongType:
+ case kCFNumberLongLongType:
+ case kCFNumberCFIndexType:
+ default:
+ theResult = [[inNumber stringValue] dataUsingEncoding:NSASCIIStringEncoding];
+ break;
+ }
+ return(theResult);
+ }
+
+- (NSData *)serializeString:(NSString *)inString error:(NSError **)outError
+ {
+ #pragma unused (outError)
+
+ const char *theUTF8String = [inString UTF8String];
+
+ NSMutableData *theData = [NSMutableData dataWithLength:strlen(theUTF8String) * 2 + 2];
+
+ char *theOutputStart = [theData mutableBytes];
+ char *OUT = theOutputStart;
+
+ *OUT++ = '"';
+
+ for (const char *IN = theUTF8String; IN && *IN != '\0'; ++IN)
+ {
+ switch (*IN)
+ {
+ case '\\':
+ {
+ *OUT++ = '\\';
+ *OUT++ = '\\';
+ }
+ break;
+ case '\"':
+ {
+ *OUT++ = '\\';
+ *OUT++ = '\"';
+ }
+ break;
+ case '/':
+ {
+ *OUT++ = '\\';
+ *OUT++ = '/';
+ }
+ break;
+ case '\b':
+ {
+ *OUT++ = '\\';
+ *OUT++ = 'b';
+ }
+ break;
+ case '\f':
+ {
+ *OUT++ = '\\';
+ *OUT++ = 'f';
+ }
+ break;
+ case '\n':
+ {
+ *OUT++ = '\\';
+ *OUT++ = 'n';
+ }
+ break;
+ case '\r':
+ {
+ *OUT++ = '\\';
+ *OUT++ = 'r';
+ }
+ break;
+ case '\t':
+ {
+ *OUT++ = '\\';
+ *OUT++ = 't';
+ }
+ break;
+ default:
+ {
+ *OUT++ = *IN;
+ }
+ break;
+ }
+ }
+
+ *OUT++ = '"';
+
+ theData.length = OUT - theOutputStart;
+ return(theData);
+ }
+
+- (NSData *)serializeArray:(NSArray *)inArray error:(NSError **)outError
+ {
+ NSMutableData *theData = [NSMutableData data];
+
+ [theData appendBytes:"[" length:1];
+
+ NSEnumerator *theEnumerator = [inArray objectEnumerator];
+ id theValue = NULL;
+ NSUInteger i = 0;
+ while ((theValue = [theEnumerator nextObject]) != NULL)
+ {
+ NSData *theValueData = [self serializeObject:theValue error:outError];
+ if (theValueData == NULL)
+ {
+ return(NULL);
+ }
+ [theData appendData:theValueData];
+ if (++i < [inArray count])
+ [theData appendBytes:"," length:1];
+ }
+
+ [theData appendBytes:"]" length:1];
+
+ return(theData);
+ }
+
+- (NSData *)serializeDictionary:(NSDictionary *)inDictionary error:(NSError **)outError
+ {
+ NSMutableData *theData = [NSMutableData data];
+
+ [theData appendBytes:"{" length:1];
+
+ NSArray *theKeys = [inDictionary allKeys];
+ NSEnumerator *theEnumerator = [theKeys objectEnumerator];
+ NSString *theKey = NULL;
+ while ((theKey = [theEnumerator nextObject]) != NULL)
+ {
+ id theValue = [inDictionary objectForKey:theKey];
+
+ NSData *theKeyData = [self serializeString:theKey error:outError];
+ if (theKeyData == NULL)
+ {
+ return(NULL);
+ }
+ NSData *theValueData = [self serializeObject:theValue error:outError];
+ if (theValueData == NULL)
+ {
+ return(NULL);
+ }
+
+
+ [theData appendData:theKeyData];
+ [theData appendBytes:":" length:1];
+ [theData appendData:theValueData];
+
+ if (theKey != [theKeys lastObject])
+ [theData appendData:[@"," dataUsingEncoding:NSASCIIStringEncoding]];
+ }
+
+ [theData appendBytes:"}" length:1];
+
+ return(theData);
+ }
+
+@end
diff --git a/Classes/Source/JSON/JSONRepresentation.h b/Classes/Source/JSON/JSONRepresentation.h
new file mode 100644
index 0000000..a83d76f
--- /dev/null
+++ b/Classes/Source/JSON/JSONRepresentation.h
@@ -0,0 +1,18 @@
+//
+// JSONRepresentation.h
+// TouchJSON
+//
+// Created by Jonathan Wight on 10/15/10.
+// Copyright 2010 toxicsoftware.com. All rights reserved.
+//
+
+#import <Foundation/Foundation.h>
+
+@protocol JSONRepresentation
+
+@optional
+- (id)initWithJSONDataRepresentation:(NSData *)inJSONData;
+
+- (NSData *)JSONDataRepresentation;
+
+@end