aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGravatar gtm.daemon <gtm.daemon@7dc7ac4e-7543-0410-b95c-c1676fc8e2a3>2009-05-07 06:00:37 +0000
committerGravatar gtm.daemon <gtm.daemon@7dc7ac4e-7543-0410-b95c-c1676fc8e2a3>2009-05-07 06:00:37 +0000
commitf963db9cc70dc2ffc7a1d29895b624ce33a68d75 (patch)
tree7a569b359421272a39312250a37c8553f26a4863
parente9fe951720e98ca768e2c51d05fa257ecfd6b894 (diff)
[Author: dmaclach]
Added some support for scanning JSON. We don't parse it, but we scan out blobs that you can then pass to a parser. DELTA=219 (219 added, 0 deleted, 0 changed) R=thomasvl
-rw-r--r--Foundation/GTMNSScanner+JSON.h38
-rw-r--r--Foundation/GTMNSScanner+JSON.m83
-rw-r--r--Foundation/GTMNSScanner+JSONTest.m125
-rw-r--r--GTM.xcodeproj/project.pbxproj12
-rw-r--r--GTMiPhone.xcodeproj/project.pbxproj10
-rw-r--r--ReleaseNotes.txt4
6 files changed, 271 insertions, 1 deletions
diff --git a/Foundation/GTMNSScanner+JSON.h b/Foundation/GTMNSScanner+JSON.h
new file mode 100644
index 0000000..22446c1
--- /dev/null
+++ b/Foundation/GTMNSScanner+JSON.h
@@ -0,0 +1,38 @@
+//
+// GTMNSScanner+JSON.h
+//
+// Copyright 2009 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License. You may obtain a copy
+// of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations under
+// the License.
+//
+
+#import <Foundation/Foundation.h>
+
+// Utilities for NSScanner containing JSON
+@interface NSScanner (GTMNSScannerJSONAdditions)
+
+// Grabs the first JSON Object (dictionary) that it finds and returns it
+// in jsonString. We don't parse the json, we just return the first valid JSON
+// dictionary we find. There are several other JSON parser packages that
+// will actually parse the json for you. We recommend json-framework
+// http://code.google.com/p/json-framework/
+- (BOOL)gtm_scanJSONObjectString:(NSString **)jsonString;
+
+// Grabs the first JSON Array (array) that it finds and returns it
+// in jsonString. We don't parse the json, we just return the first valid JSON
+// array we find. There are several other JSON parser packages that
+// will actually parse the json for you. We recommend json-framework
+// http://code.google.com/p/json-framework/
+- (BOOL)gtm_scanJSONArrayString:(NSString**)jsonString;
+
+@end
diff --git a/Foundation/GTMNSScanner+JSON.m b/Foundation/GTMNSScanner+JSON.m
new file mode 100644
index 0000000..1216698
--- /dev/null
+++ b/Foundation/GTMNSScanner+JSON.m
@@ -0,0 +1,83 @@
+//
+// GTMNSScanner+JSON.m
+//
+// Copyright 2009 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License. You may obtain a copy
+// of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations under
+// the License.
+//
+
+#import "GTMDefines.h"
+#import "GTMNSScanner+JSON.h"
+
+@implementation NSScanner (GTMNSScannerJSONAdditions)
+
+- (BOOL)gtm_scanJSONString:(NSString **)jsonString
+ startChar:(unichar)startChar
+ endChar:(unichar)endChar {
+ BOOL isGood = NO;
+ NSRange jsonRange = { NSNotFound, 0 };
+ NSString *scanString = [self string];
+ NSUInteger startLocation = [self scanLocation];
+ NSUInteger length = [scanString length];
+ NSUInteger blockOpen = 0;
+ NSCharacterSet *charsToSkip = [self charactersToBeSkipped];
+ BOOL inQuoteMode = NO;
+ NSUInteger i;
+ for (i = startLocation; i < length; ++i) {
+ unichar jsonChar = [scanString characterAtIndex:i];
+ if (jsonChar == startChar && !inQuoteMode) {
+ if (blockOpen == 0) {
+ jsonRange.location = i;
+ }
+ blockOpen += 1;
+ } else if (blockOpen == 0) {
+ // If we haven't opened our block skip over any characters in
+ // charsToSkip.
+ if (![charsToSkip characterIsMember:jsonChar]) {
+ break;
+ }
+ } else if (jsonChar == endChar && !inQuoteMode) {
+ blockOpen -= 1;
+ if (blockOpen == 0) {
+ i += 1; // Move onto next character
+ jsonRange.length = i - jsonRange.location;
+ break;
+ }
+ } else {
+ if (jsonChar == '"') {
+ inQuoteMode = !inQuoteMode;
+ } else if (inQuoteMode && jsonChar == '\\') {
+ // Skip the escaped character if it isn't the last one
+ if (i < length - 1) ++i;
+ }
+ }
+ }
+ [self setScanLocation:i];
+ if (blockOpen == 0 && jsonRange.location != NSNotFound) {
+ isGood = YES;
+ if (jsonString) {
+ *jsonString = [scanString substringWithRange:jsonRange];
+ }
+ }
+ return isGood;
+}
+
+- (BOOL)gtm_scanJSONObjectString:(NSString **)jsonString {
+ return [self gtm_scanJSONString:jsonString startChar:'{' endChar:'}'];
+}
+
+- (BOOL)gtm_scanJSONArrayString:(NSString**)jsonString {
+ return [self gtm_scanJSONString:jsonString startChar:'[' endChar:']'];
+}
+
+@end
diff --git a/Foundation/GTMNSScanner+JSONTest.m b/Foundation/GTMNSScanner+JSONTest.m
new file mode 100644
index 0000000..08f5db1
--- /dev/null
+++ b/Foundation/GTMNSScanner+JSONTest.m
@@ -0,0 +1,125 @@
+//
+// GTMNSScanner+JSONTest.m
+//
+// Copyright 2005-2008 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License. You may obtain a copy
+// of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations under
+// the License.
+//
+
+#import "GTMSenTestCase.h"
+#import "GTMNSScanner+JSON.h"
+
+@interface GTMNSScanner_JSONTest : GTMTestCase
+@end
+
+struct {
+ NSString *testString_;
+ NSString *resultString_;
+ BOOL isObject_;
+} testStrings[] = {
+ { @"", nil, NO },
+ { @"\"Empty String\"", nil, NO },
+ { @"[\"Unclosed array\"", nil, NO },
+ { @"[\"escape at end of unfinished string\\", nil, NO },
+ { @"junk, [\"Unclosed array with junk before\"", nil, NO },
+ { @"\"Unopened array\"]", nil, NO },
+ { @"\"Unopened array with junk after\"] junk", nil, NO },
+ { @"[\"array\"]", @"[\"array\"]", NO },
+ { @"junk [\"array with junk\"]", @"[\"array with junk\"]", NO },
+ { @"[\"array with junk\"], junk", @"[\"array with junk\"]", NO },
+ { @"[[[\"nested array\"]]]", @"[[[\"nested array\"]]]", NO },
+ { @"[[[\"badly nested array\"]]", nil, NO },
+ { @"[[[\"over nested array\"]]]]", @"[[[\"over nested array\"]]]", NO },
+ { @"[{]", @"[{]", NO },
+ { @"[\"closer in quotes\":\"]\"]", @"[\"closer in quotes\":\"]\"]", NO },
+ { @"[\"escaped closer\":\\]]", @"[\"escaped closer\":\\]", NO },
+ { @"[\"double escape\":\\\\]", @"[\"double escape\":\\\\]", NO },
+ { @"[\"doub esc quote\":\"\\\"]\"]", @"[\"doub esc quote\":\"\\\"]\"]", NO },
+ { @"[\"opener in quotes\":\"[\"]", @"[\"opener in quotes\":\"[\"]", NO },
+ { @"[\"escaped opener\":\\[]", nil, NO },
+ { @"[\"escaped opener\":\\[]]", @"[\"escaped opener\":\\[]]", NO },
+ { @"[\"doub esc quote\":\"\\\"[\"]", @"[\"doub esc quote\":\"\\\"[\"]", NO },
+ { @"{\"Unclosed object\"", nil, YES },
+ { @"junk, {\"Unclosed object with junk before\"", nil, YES },
+ { @"\"Unopened object\"}", nil, YES },
+ { @"\"Unopened object with junk after\"} junk", nil, YES },
+ { @"{\"object\"}", @"{\"object\"}", YES },
+ { @"junk, {\"object with junk\"}", @"{\"object with junk\"}", YES },
+ { @"{\"object with junk\"}, junk", @"{\"object with junk\"}", YES },
+ { @"{{{\"nested object\"}}}", @"{{{\"nested object\"}}}", YES },
+ { @"{{{\"badly nested object\"}}", nil, YES },
+ { @"{{{\"over nested object\"}}}}", @"{{{\"over nested object\"}}}", YES },
+ { @"{[}", @"{[}", YES },
+ { @"{\"closer in quotes\":\"}\"}", @"{\"closer in quotes\":\"}\"}", YES },
+ { @"{\"escaped closer\":\\}}", @"{\"escaped closer\":\\}", YES },
+ { @"{\"double escape\":\\\\}", @"{\"double escape\":\\\\}", YES },
+ { @"{\"doub esc quote\":\"\\\"}\"}", @"{\"doub esc quote\":\"\\\"}\"}", YES },
+ { @"{\"opener in quotes\":\"{\"}", @"{\"opener in quotes\":\"{\"}", YES },
+ { @"{\"escaped opener\":\\{}", nil, YES },
+ { @"{\"escaped opener\":\\{}}", @"{\"escaped opener\":\\{}}", YES },
+ { @"{\"doub esc quote\":\"\\\"{\"}", @"{\"doub esc quote\":\"\\\"{\"}", YES },
+};
+
+@implementation GTMNSScanner_JSONTest
+
+- (void)testJSONObject {
+ NSCharacterSet *set = [[NSCharacterSet illegalCharacterSet] invertedSet];
+ for (size_t i = 0; i < sizeof(testStrings) / sizeof(testStrings[0]); ++i) {
+ NSScanner *scanner
+ = [NSScanner scannerWithString:testStrings[i].testString_];
+ [scanner setCharactersToBeSkipped:set];
+ NSString *array = nil;
+ BOOL goodArray = [scanner gtm_scanJSONArrayString:&array];
+ scanner = [NSScanner scannerWithString:testStrings[i].testString_];
+ [scanner setCharactersToBeSkipped:set];
+ NSString *object = nil;
+ BOOL goodObject = [scanner gtm_scanJSONObjectString:&object];
+ if (testStrings[i].resultString_) {
+ if (testStrings[i].isObject_) {
+ STAssertEqualStrings(testStrings[i].resultString_,
+ object, @"Test String: %@", testStrings[i]);
+ STAssertNil(array, @"Test String: %@", testStrings[i]);
+ STAssertTrue(goodObject, @"Test String: %@", testStrings[i]);
+ STAssertFalse(goodArray, @"Test String: %@", testStrings[i]);
+ } else {
+ STAssertEqualStrings(testStrings[i].resultString_, array,
+ @"Test String: %@", testStrings[i]);
+ STAssertNil(object, @"Test String: %@", testStrings[i]);
+ STAssertTrue(goodArray, @"Test String: %@", testStrings[i]);
+ STAssertFalse(goodObject, @"Test String: %@", testStrings[i]);
+ }
+ } else {
+ STAssertNil(object, @"Test String: %@", testStrings[i]);
+ STAssertNil(array, @"Test String: %@", testStrings[i]);
+ STAssertFalse(goodArray, @"Test String: %@", testStrings[i]);
+ STAssertFalse(goodObject, @"Test String: %@", testStrings[i]);
+ }
+ }
+}
+
+- (void)testScanningCharacters {
+ NSCharacterSet *alphaSet = [NSCharacterSet alphanumericCharacterSet];
+ NSString *testString = @"asdfasdf[]:,";
+ NSScanner *scanner = [NSScanner scannerWithString:testString];
+ [scanner setCharactersToBeSkipped:alphaSet];
+ NSString *array = nil;
+ STAssertTrue([scanner gtm_scanJSONArrayString:&array], nil);
+ STAssertEqualStrings(array, @"[]", nil);
+ NSString *nextValue = nil;
+ STAssertTrue([scanner scanString:@":," intoString:&nextValue], nil);
+ STAssertEqualStrings(@":,", nextValue, nil);
+ scanner = [NSScanner scannerWithString:testString];
+ STAssertFalse([scanner gtm_scanJSONArrayString:&array], nil);
+}
+
+@end
diff --git a/GTM.xcodeproj/project.pbxproj b/GTM.xcodeproj/project.pbxproj
index d2a0e17..f83228a 100644
--- a/GTM.xcodeproj/project.pbxproj
+++ b/GTM.xcodeproj/project.pbxproj
@@ -172,6 +172,9 @@
8BC045C20DAE899100C2D1CA /* GTMGeometryUtilsTest.m in Sources */ = {isa = PBXBuildFile; fileRef = F48FE2800D198D0E009257D2 /* GTMGeometryUtilsTest.m */; };
8BC046B90DAE8C4B00C2D1CA /* ApplicationServices.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8BC046B80DAE8C4B00C2D1CA /* ApplicationServices.framework */; };
8BC04CD80DB003D800C2D1CA /* GTMMethodCheck.h in Headers */ = {isa = PBXBuildFile; fileRef = 8B6F31F40DA3489B0052CA40 /* GTMMethodCheck.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 8BD35B910FB22980009058F5 /* GTMNSScanner+JSON.h in Headers */ = {isa = PBXBuildFile; fileRef = 8BD35B8E0FB22980009058F5 /* GTMNSScanner+JSON.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 8BD35B920FB22980009058F5 /* GTMNSScanner+JSON.m in Sources */ = {isa = PBXBuildFile; fileRef = 8BD35B8F0FB22980009058F5 /* GTMNSScanner+JSON.m */; };
+ 8BD35B940FB22986009058F5 /* GTMNSScanner+JSONTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 8BD35B900FB22980009058F5 /* GTMNSScanner+JSONTest.m */; };
8BE281B00DEC7E930035B3F8 /* GTMNSAppleScript+Handler.h in Headers */ = {isa = PBXBuildFile; fileRef = 8B3344190DBF7A36009FD32C /* GTMNSAppleScript+Handler.h */; settings = {ATTRIBUTES = (Public, ); }; };
8BE281B10DEC7E930035B3F8 /* GTMNSAppleEventDescriptor+Handler.h in Headers */ = {isa = PBXBuildFile; fileRef = 8B33441C0DBF7A36009FD32C /* GTMNSAppleEventDescriptor+Handler.h */; settings = {ATTRIBUTES = (Public, ); }; };
8BE281B20DEC7E930035B3F8 /* GTMNSAppleEventDescriptor+Foundation.h in Headers */ = {isa = PBXBuildFile; fileRef = 8B33441F0DBF7A36009FD32C /* GTMNSAppleEventDescriptor+Foundation.h */; settings = {ATTRIBUTES = (Public, ); }; };
@@ -482,6 +485,9 @@
8BAA9E370F7C19D500DF4F12 /* English */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = English; path = UnitTesting/GTMUIUnitTestingHarness/English.lproj/MainMenu.xib; sourceTree = "<group>"; };
8BC046B80DAE8C4B00C2D1CA /* ApplicationServices.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = ApplicationServices.framework; path = /System/Library/Frameworks/ApplicationServices.framework; sourceTree = "<absolute>"; };
8BC04D140DB0061300C2D1CA /* RunMacOSUnitTests.sh */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.sh; path = RunMacOSUnitTests.sh; sourceTree = "<group>"; };
+ 8BD35B8E0FB22980009058F5 /* GTMNSScanner+JSON.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "GTMNSScanner+JSON.h"; sourceTree = "<group>"; };
+ 8BD35B8F0FB22980009058F5 /* GTMNSScanner+JSON.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "GTMNSScanner+JSON.m"; sourceTree = "<group>"; };
+ 8BD35B900FB22980009058F5 /* GTMNSScanner+JSONTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "GTMNSScanner+JSONTest.m"; sourceTree = "<group>"; };
8BE2836B0DED0F130035B3F8 /* GTMFourCharCode.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GTMFourCharCode.m; sourceTree = "<group>"; };
8BE2836C0DED0F130035B3F8 /* GTMFourCharCodeTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GTMFourCharCodeTest.m; sourceTree = "<group>"; };
8BE2836D0DED0F130035B3F8 /* GTMFourCharCode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GTMFourCharCode.h; sourceTree = "<group>"; };
@@ -983,6 +989,9 @@
8B6C15910F356E6400E51E5D /* GTMNSObject+KeyValueObserving.h */,
8B6C15920F356E6400E51E5D /* GTMNSObject+KeyValueObserving.m */,
8B6C161B0F3580DA00E51E5D /* GTMNSObject+KeyValueObservingTest.m */,
+ 8BD35B8E0FB22980009058F5 /* GTMNSScanner+JSON.h */,
+ 8BD35B8F0FB22980009058F5 /* GTMNSScanner+JSON.m */,
+ 8BD35B900FB22980009058F5 /* GTMNSScanner+JSONTest.m */,
F42597760E23FE3A003BEA3E /* GTMNSString+FindFolder.h */,
F42597770E23FE3A003BEA3E /* GTMNSString+FindFolder.m */,
F42597780E23FE3A003BEA3E /* GTMNSString+FindFolderTest.m */,
@@ -1178,6 +1187,7 @@
10998EF50F4B5D1A007F179D /* GTMTransientRootPortProxy.h in Headers */,
8B40994B0F93C5CC00DF540E /* GTMUILocalizer.h in Headers */,
8BFE13B60FB0F2C0001BE894 /* GTMABAddressBook.h in Headers */,
+ 8BD35B910FB22980009058F5 /* GTMNSScanner+JSON.h in Headers */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -1595,6 +1605,7 @@
F95B567F0F46208E0051A6F1 /* GTMSQLiteTest.m in Sources */,
10998F8B0F4B5F1B007F179D /* GTMTransientRootProxyTest.m in Sources */,
108930850F4CCB380018D4A0 /* GTMTransientRootPortProxyTest.m in Sources */,
+ 8BD35B940FB22986009058F5 /* GTMNSScanner+JSONTest.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -1659,6 +1670,7 @@
10998EF40F4B5D1A007F179D /* GTMTransientRootPortProxy.m in Sources */,
8B40994C0F93C5CC00DF540E /* GTMUILocalizer.m in Sources */,
8BFE13B70FB0F2C0001BE894 /* GTMABAddressBook.m in Sources */,
+ 8BD35B920FB22980009058F5 /* GTMNSScanner+JSON.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
diff --git a/GTMiPhone.xcodeproj/project.pbxproj b/GTMiPhone.xcodeproj/project.pbxproj
index 8fc1472..24c7597 100644
--- a/GTMiPhone.xcodeproj/project.pbxproj
+++ b/GTMiPhone.xcodeproj/project.pbxproj
@@ -67,6 +67,8 @@
8BC04A750DAF145200C2D1CA /* GTMSystemVersion.m in Sources */ = {isa = PBXBuildFile; fileRef = 8BC04A740DAF145200C2D1CA /* GTMSystemVersion.m */; };
8BC04D480DB0088500C2D1CA /* libz.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 8BC04D470DB0088500C2D1CA /* libz.dylib */; };
8BC04DE80DB023D400C2D1CA /* ReleaseNotes.txt in Resources */ = {isa = PBXBuildFile; fileRef = 8BC04DE70DB023D400C2D1CA /* ReleaseNotes.txt */; };
+ 8BD35C920FB234E1009058F5 /* GTMNSScanner+JSON.m in Sources */ = {isa = PBXBuildFile; fileRef = 8BD35C900FB234E1009058F5 /* GTMNSScanner+JSON.m */; };
+ 8BD35C930FB234E1009058F5 /* GTMNSScanner+JSONTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 8BD35C910FB234E1009058F5 /* GTMNSScanner+JSONTest.m */; };
8BDA25130E759A6400C9769D /* GTMHTTPServerTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 8B3AA9210E033624007E31B5 /* GTMHTTPServerTest.m */; };
8BDA25140E759A6500C9769D /* GTMNSData+zlibTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 8BC047800DAE928A00C2D1CA /* GTMNSData+zlibTest.m */; };
8BE839890E89C74B00C611B0 /* GTMDebugThreadValidation.m in Sources */ = {isa = PBXBuildFile; fileRef = 8BE839870E89C74A00C611B0 /* GTMDebugThreadValidation.m */; };
@@ -188,6 +190,9 @@
8BC04A740DAF145200C2D1CA /* GTMSystemVersion.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GTMSystemVersion.m; sourceTree = "<group>"; };
8BC04D470DB0088500C2D1CA /* libz.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libz.dylib; path = usr/lib/libz.dylib; sourceTree = SDKROOT; };
8BC04DE70DB023D400C2D1CA /* ReleaseNotes.txt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = ReleaseNotes.txt; sourceTree = "<group>"; };
+ 8BD35C8F0FB234E1009058F5 /* GTMNSScanner+JSON.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "GTMNSScanner+JSON.h"; sourceTree = "<group>"; };
+ 8BD35C900FB234E1009058F5 /* GTMNSScanner+JSON.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "GTMNSScanner+JSON.m"; sourceTree = "<group>"; };
+ 8BD35C910FB234E1009058F5 /* GTMNSScanner+JSONTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "GTMNSScanner+JSONTest.m"; sourceTree = "<group>"; };
8BE839870E89C74A00C611B0 /* GTMDebugThreadValidation.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GTMDebugThreadValidation.m; sourceTree = "<group>"; };
8BE839880E89C74A00C611B0 /* GTMDebugThreadValidation.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GTMDebugThreadValidation.h; sourceTree = "<group>"; };
8BE83A650E8B059A00C611B0 /* GTMDebugThreadValidationTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GTMDebugThreadValidationTest.m; sourceTree = "<group>"; };
@@ -345,6 +350,9 @@
8B6C18710F3769D200E51E5D /* GTMNSObject+KeyValueObserving.h */,
8B6C18720F3769D200E51E5D /* GTMNSObject+KeyValueObserving.m */,
8B6C18730F3769D200E51E5D /* GTMNSObject+KeyValueObservingTest.m */,
+ 8BD35C8F0FB234E1009058F5 /* GTMNSScanner+JSON.h */,
+ 8BD35C900FB234E1009058F5 /* GTMNSScanner+JSON.m */,
+ 8BD35C910FB234E1009058F5 /* GTMNSScanner+JSONTest.m */,
8BC047870DAE928A00C2D1CA /* GTMNSString+HTML.h */,
8BC047880DAE928A00C2D1CA /* GTMNSString+HTML.m */,
8BC047890DAE928A00C2D1CA /* GTMNSString+HTMLTest.m */,
@@ -626,6 +634,8 @@
8B6C18750F3769D200E51E5D /* GTMNSObject+KeyValueObservingTest.m in Sources */,
8BFE15C60FB0F764001BE894 /* GTMABAddressBook.m in Sources */,
8BFE15C80FB0F764001BE894 /* GTMABAddressBookTest.m in Sources */,
+ 8BD35C920FB234E1009058F5 /* GTMNSScanner+JSON.m in Sources */,
+ 8BD35C930FB234E1009058F5 /* GTMNSScanner+JSONTest.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
diff --git a/ReleaseNotes.txt b/ReleaseNotes.txt
index 24b836f..643eea9 100644
--- a/ReleaseNotes.txt
+++ b/ReleaseNotes.txt
@@ -288,7 +288,9 @@ Changes since 1.5.1
- Moved GTMABAddressBook out of iPhone and into the AddressBook directory,
because it now works on both the Desktop and the iPhone giving you a single
interface to do AddressBook work on both platforms.
-
+
+- Added GTMNSScanner+JSON for scanning out JSON objects and arrays. We don't
+ parse JSON as there are several other frameworks out there for doing that.
Release 1.5.1
Changes since 1.5.0