Added JSONKit support
Summary: React Native will now use JSONKit if it's already available in the project, otherwise it will fall back to using NSJSONSerialization as before. This provides a small performance boost to JSON parsing in some cases.
This commit is contained in:
parent
f2d65ea85b
commit
5db42643cf
|
@ -20,6 +20,7 @@
|
|||
13B07FBD1A68108700A75B9A /* LaunchScreen.xib in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB11A68108700A75B9A /* LaunchScreen.xib */; };
|
||||
13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB51A68108700A75B9A /* Images.xcassets */; };
|
||||
13B07FC11A68108700A75B9A /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB71A68108700A75B9A /* main.m */; };
|
||||
13DB03481B5D2ED500C27245 /* RCTJSONTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 13DB03471B5D2ED500C27245 /* RCTJSONTests.m */; };
|
||||
141FC1211B222EBB004D5FFB /* IntegrationTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 141FC1201B222EBB004D5FFB /* IntegrationTests.m */; };
|
||||
143BC5A11B21E45C00462512 /* UIExplorerSnapshotTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 143BC5A01B21E45C00462512 /* UIExplorerSnapshotTests.m */; };
|
||||
144D21241B2204C5006DB32B /* RCTClippingTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 144D21231B2204C5006DB32B /* RCTClippingTests.m */; };
|
||||
|
@ -174,6 +175,7 @@
|
|||
13B07FB61A68108700A75B9A /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = Info.plist; path = UIExplorer/Info.plist; sourceTree = "<group>"; };
|
||||
13B07FB71A68108700A75B9A /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = main.m; path = UIExplorer/main.m; sourceTree = "<group>"; };
|
||||
13CC9D481AEED2B90020D1C2 /* RCTSettings.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTSettings.xcodeproj; path = ../../Libraries/Settings/RCTSettings.xcodeproj; sourceTree = "<group>"; };
|
||||
13DB03471B5D2ED500C27245 /* RCTJSONTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTJSONTests.m; sourceTree = "<group>"; };
|
||||
141FC1201B222EBB004D5FFB /* IntegrationTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = IntegrationTests.m; sourceTree = "<group>"; };
|
||||
143BC57E1B21E18100462512 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
||||
143BC5811B21E18100462512 /* testLayoutExampleSnapshot_1@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "testLayoutExampleSnapshot_1@2x.png"; sourceTree = "<group>"; };
|
||||
|
@ -350,16 +352,17 @@
|
|||
children = (
|
||||
1497CFA41B21F5E400C1F8F2 /* RCTAllocationTests.m */,
|
||||
1497CFA51B21F5E400C1F8F2 /* RCTBridgeTests.m */,
|
||||
138D6A151B53CD440074A87E /* RCTCacheTests.m */,
|
||||
144D21231B2204C5006DB32B /* RCTClippingTests.m */,
|
||||
1497CFA61B21F5E400C1F8F2 /* RCTContextExecutorTests.m */,
|
||||
1497CFA71B21F5E400C1F8F2 /* RCTConvert_NSURLTests.m */,
|
||||
1497CFA81B21F5E400C1F8F2 /* RCTConvert_UIFontTests.m */,
|
||||
1497CFA91B21F5E400C1F8F2 /* RCTEventDispatcherTests.m */,
|
||||
1300627E1B59179B0043FE5A /* RCTGzipTests.m */,
|
||||
13DB03471B5D2ED500C27245 /* RCTJSONTests.m */,
|
||||
138D6A161B53CD440074A87E /* RCTShadowViewTests.m */,
|
||||
1497CFAA1B21F5E400C1F8F2 /* RCTSparseArrayTests.m */,
|
||||
1497CFAB1B21F5E400C1F8F2 /* RCTUIManagerTests.m */,
|
||||
138D6A151B53CD440074A87E /* RCTCacheTests.m */,
|
||||
1300627E1B59179B0043FE5A /* RCTGzipTests.m */,
|
||||
143BC57E1B21E18100462512 /* Info.plist */,
|
||||
14D6D7101B220EB3001FB087 /* libOCMock.a */,
|
||||
14D6D7011B220AE3001FB087 /* OCMock */,
|
||||
|
@ -793,6 +796,7 @@
|
|||
1497CFB11B21F5E400C1F8F2 /* RCTEventDispatcherTests.m in Sources */,
|
||||
1497CFB31B21F5E400C1F8F2 /* RCTUIManagerTests.m in Sources */,
|
||||
138D6A171B53CD440074A87E /* RCTCacheTests.m in Sources */,
|
||||
13DB03481B5D2ED500C27245 /* RCTJSONTests.m in Sources */,
|
||||
1497CFAC1B21F5E400C1F8F2 /* RCTAllocationTests.m in Sources */,
|
||||
138D6A181B53CD440074A87E /* RCTShadowViewTests.m in Sources */,
|
||||
);
|
||||
|
|
|
@ -0,0 +1,81 @@
|
|||
/**
|
||||
* The examples provided by Facebook are for non-commercial testing and
|
||||
* evaluation purposes only.
|
||||
*
|
||||
* Facebook reserves all rights not expressly granted.
|
||||
*
|
||||
* 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 NON INFRINGEMENT. IN NO EVENT SHALL
|
||||
* FACEBOOK 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 <XCTest/XCTest.h>
|
||||
#import "RCTUtils.h"
|
||||
|
||||
@interface RCTJSONTests : XCTestCase
|
||||
|
||||
@end
|
||||
|
||||
@implementation RCTJSONTests
|
||||
|
||||
- (void)testEncodingObject
|
||||
{
|
||||
NSDictionary *obj = @{@"foo": @"bar"};
|
||||
NSString *json = @"{\"foo\":\"bar\"}";
|
||||
XCTAssertEqualObjects(json, RCTJSONStringify(obj, NULL));
|
||||
}
|
||||
|
||||
- (void)testEncodingArray
|
||||
{
|
||||
NSArray *array = @[@"foo", @"bar"];
|
||||
NSString *json = @"[\"foo\",\"bar\"]";
|
||||
XCTAssertEqualObjects(json, RCTJSONStringify(array, NULL));
|
||||
}
|
||||
|
||||
- (void)testEncodingString
|
||||
{
|
||||
NSString *text = @"Hello\nWorld";
|
||||
NSString *json = @"\"Hello\\nWorld\"";
|
||||
XCTAssertEqualObjects(json, RCTJSONStringify(text, NULL));
|
||||
}
|
||||
|
||||
- (void)testDecodingObject
|
||||
{
|
||||
NSDictionary *obj = @{@"foo": @"bar"};
|
||||
NSString *json = @"{\"foo\":\"bar\"}";
|
||||
XCTAssertEqualObjects(obj, RCTJSONParse(json, NULL));
|
||||
}
|
||||
|
||||
- (void)testDecodingArray
|
||||
{
|
||||
NSArray *array = @[@"foo", @"bar"];
|
||||
NSString *json = @"[\"foo\",\"bar\"]";
|
||||
XCTAssertEqualObjects(array, RCTJSONParse(json, NULL));
|
||||
}
|
||||
|
||||
- (void)testDecodingString
|
||||
{
|
||||
NSString *text = @"Hello\nWorld";
|
||||
NSString *json = @"\"Hello\\nWorld\"";
|
||||
XCTAssertEqualObjects(text, RCTJSONParse(json, NULL));
|
||||
}
|
||||
|
||||
- (void)testDecodingMutableArray
|
||||
{
|
||||
NSString *json = @"[1,2,3]";
|
||||
NSMutableArray *array = RCTJSONParseMutable(json, NULL);
|
||||
XCTAssertNoThrow([array addObject:@4]);
|
||||
XCTAssertEqualObjects(array, (@[@1, @2, @3, @4]));
|
||||
}
|
||||
|
||||
- (void)testLeadingWhitespace
|
||||
{
|
||||
NSDictionary *obj = @{@"foo": @"bar"};
|
||||
NSString *json = @" \r\n\t{\"foo\":\"bar\"}";
|
||||
XCTAssertEqualObjects(obj, RCTJSONParse(json, NULL));
|
||||
}
|
||||
|
||||
@end
|
|
@ -19,7 +19,6 @@
|
|||
RCT_EXTERN NSString *RCTJSONStringify(id jsonObject, NSError **error);
|
||||
RCT_EXTERN id RCTJSONParse(NSString *jsonString, NSError **error);
|
||||
RCT_EXTERN id RCTJSONParseMutable(NSString *jsonString, NSError **error);
|
||||
RCT_EXTERN id RCTJSONParseWithOptions(NSString *jsonString, NSError **error, NSJSONReadingOptions options);
|
||||
|
||||
// Strip non JSON-safe values from an object graph
|
||||
RCT_EXTERN id RCTJSONClean(id object);
|
||||
|
|
|
@ -23,34 +23,99 @@
|
|||
|
||||
NSString *RCTJSONStringify(id jsonObject, NSError **error)
|
||||
{
|
||||
NSData *jsonData = [NSJSONSerialization dataWithJSONObject:jsonObject options:(NSJSONWritingOptions)NSJSONReadingAllowFragments error:error];
|
||||
static SEL JSONKitSelector = NULL;
|
||||
static NSSet *collectionTypes;
|
||||
static dispatch_once_t onceToken;
|
||||
dispatch_once(&onceToken, ^{
|
||||
SEL selector = NSSelectorFromString(@"JSONStringWithOptions:error:");
|
||||
if ([NSDictionary instancesRespondToSelector:selector]) {
|
||||
JSONKitSelector = selector;
|
||||
collectionTypes = [NSSet setWithObjects:
|
||||
[NSArray class], [NSMutableArray class],
|
||||
[NSDictionary class], [NSMutableDictionary class], nil];
|
||||
}
|
||||
});
|
||||
|
||||
// Use JSONKit if available and object is not a fragment
|
||||
if (JSONKitSelector && [collectionTypes containsObject:[jsonObject classForCoder]]) {
|
||||
return ((NSString *(*)(id, SEL, int, NSError **))objc_msgSend)(jsonObject, JSONKitSelector, 0, error);
|
||||
}
|
||||
|
||||
// Use Foundation JSON method
|
||||
NSData *jsonData = [NSJSONSerialization
|
||||
dataWithJSONObject:jsonObject
|
||||
options:(NSJSONWritingOptions)NSJSONReadingAllowFragments
|
||||
error:error];
|
||||
return jsonData ? [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding] : nil;
|
||||
}
|
||||
|
||||
id RCTJSONParseWithOptions(NSString *jsonString, NSError **error, NSJSONReadingOptions options)
|
||||
static id _RCTJSONParse(NSString *jsonString, BOOL mutable, NSError **error)
|
||||
{
|
||||
if (!jsonString) {
|
||||
return nil;
|
||||
}
|
||||
NSData *jsonData = [jsonString dataUsingEncoding:NSUTF8StringEncoding allowLossyConversion:NO];
|
||||
if (!jsonData) {
|
||||
jsonData = [jsonString dataUsingEncoding:NSUTF8StringEncoding allowLossyConversion:YES];
|
||||
if (jsonData) {
|
||||
RCTLogWarn(@"RCTJSONParse received the following string, which could not be losslessly converted to UTF8 data: '%@'", jsonString);
|
||||
} else {
|
||||
RCTLogError(@"RCTJSONParse received invalid UTF8 data");
|
||||
return nil;
|
||||
static SEL JSONKitSelector = NULL;
|
||||
static SEL JSONKitMutableSelector = NULL;
|
||||
static dispatch_once_t onceToken;
|
||||
dispatch_once(&onceToken, ^{
|
||||
SEL selector = NSSelectorFromString(@"objectFromJSONStringWithParseOptions:error:");
|
||||
if ([NSString instancesRespondToSelector:selector]) {
|
||||
JSONKitSelector = selector;
|
||||
JSONKitMutableSelector = NSSelectorFromString(@"mutableObjectFromJSONStringWithParseOptions:error:");
|
||||
}
|
||||
});
|
||||
|
||||
if (jsonString) {
|
||||
|
||||
// Use JSONKit if available and string is not a fragment
|
||||
if (JSONKitSelector) {
|
||||
NSInteger length = jsonString.length;
|
||||
for (NSInteger i = 0; i < length; i++) {
|
||||
unichar c = [jsonString characterAtIndex:i];
|
||||
if (strchr("{[", c)) {
|
||||
static const int options = (1 << 2); // loose unicode
|
||||
SEL selector = mutable ? JSONKitMutableSelector : JSONKitSelector;
|
||||
return ((id (*)(id, SEL, int, NSError **))objc_msgSend)(jsonString, selector, options, error);
|
||||
}
|
||||
if (!strchr(" \r\n\t", c)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Use Foundation JSON method
|
||||
NSData *jsonData = [jsonString dataUsingEncoding:NSUTF8StringEncoding];
|
||||
if (!jsonData) {
|
||||
jsonData = [jsonString dataUsingEncoding:NSUTF8StringEncoding allowLossyConversion:YES];
|
||||
if (jsonData) {
|
||||
RCTLogWarn(@"RCTJSONParse received the following string, which could "
|
||||
"not be losslessly converted to UTF8 data: '%@'", jsonString);
|
||||
} else {
|
||||
NSString *errorMessage = @"RCTJSONParse received invalid UTF8 data";
|
||||
if (error) {
|
||||
*error = RCTErrorWithMessage(errorMessage);
|
||||
} else {
|
||||
RCTLogError(@"%@", errorMessage);
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
}
|
||||
NSJSONReadingOptions options = NSJSONReadingAllowFragments;
|
||||
if (mutable) {
|
||||
options |= NSJSONReadingMutableContainers;
|
||||
}
|
||||
return [NSJSONSerialization JSONObjectWithData:jsonData
|
||||
options:options
|
||||
error:error];
|
||||
}
|
||||
return [NSJSONSerialization JSONObjectWithData:jsonData options:options error:error];
|
||||
return nil;
|
||||
}
|
||||
|
||||
id RCTJSONParse(NSString *jsonString, NSError **error) {
|
||||
return RCTJSONParseWithOptions(jsonString, error, NSJSONReadingAllowFragments);
|
||||
id RCTJSONParse(NSString *jsonString, NSError **error)
|
||||
{
|
||||
return _RCTJSONParse(jsonString, NO, error);
|
||||
}
|
||||
|
||||
id RCTJSONParseMutable(NSString *jsonString, NSError **error) {
|
||||
return RCTJSONParseWithOptions(jsonString, error, NSJSONReadingMutableContainers|NSJSONReadingMutableLeaves);
|
||||
id RCTJSONParseMutable(NSString *jsonString, NSError **error)
|
||||
{
|
||||
return _RCTJSONParse(jsonString, YES, error);
|
||||
}
|
||||
|
||||
id RCTJSONClean(id object)
|
||||
|
|
Loading…
Reference in New Issue