JSON in iOS
Working with JSON is easy in iOS 5, since there’s now a great, native JSON serializer and deserializer that handles everything for you.
These classes work well, but I find it tedious to write code for creating and parsing JSON data over and over again. To make my code cleaner and get loose coupling to the native classes, I have defined a JSON serializer/deserializer protocol and implement it using the native classes.
I first created this simple protocol
#import <Foundation/Foundation.h>
@protocol ObjectSerializer <NSObject>
- (id)deserializeStringToObject:(NSString *)string;
- (NSString *)serializeObjectToString:(id)object;
@end
then created a small implementation of this protocol, which looks like this:
#import <Foundation/Foundation.h>
#import "ObjectSerializer.h"
@interface NativeJsonSerializer : NSObject<ObjectSerializer>
@end
#import "NativeJsonSerializer.h"
@implementation NativeJsonSerializer
- (id)deserializeStringToObject:(NSString *)string
{
NSData *data = [string dataUsingEncoding:NSUTF8StringEncoding];
NSError *error = nil;
id result = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingAllowFragments error:&error];
if (!result) {
NSLog(@"%@", error.description);
}
return result;
}
- (NSString *)serializeObjectToString:(id)data
{
NSError *error;
NSData *result = [NSJSONSerialization dataWithJSONObject:data options:NSJSONReadingAllowFragments|NSJSONWritingPrettyPrinted error:&error];
if (!result) {
NSLog(@"%@", error.description);
}
return [[NSString alloc] initWithData:result encoding:NSUTF8StringEncoding];
}
@end
Finally, I added some tests that test a lot of possible JSON operations:
#import <SenTestingKit/SenTestingKit.h>
@interface NativeJsonSerializerTests : SenTestCase
@end
#import "NativeJsonSerializerTests.h"
#import "NativeJsonSerializer.h"
@implementation NativeJsonSerializerTests
NativeJsonSerializer *_serializer;
- (void)setUp {
[super setUp];
_serializer = [[NativeJsonSerializer alloc] init];
}
- (void)tearDown {
// Tear-down code here.
[super tearDown];
}
- (void)test_serializerShouldDeserializeArray {
NSArray *result = [_serializer deserializeStringToObject:@"[\"foo\",\"bar\"]"];
bool error = FALSE;
error = error && !(result.count == 2);
error = error && !([(NSString *)[result objectAtIndex:0] isEqualToString:@"foo"]);
error = error && !([(NSString *)[result objectAtIndex:1] isEqualToString:@"bar"]);
if (error)
STFail(@"NativeJsonSerializer could not deserialize array");
}
- (void)test_serializerShouldDeserializeDictionary {
NSDictionary *result = [_serializer deserializeStringToObject:@"{\"foo\":\"bar\",\"bar\":\"foo\"}"];
bool error = FALSE;
error = error && !(result.count == 2);
error = error && !([(NSString *)[result objectForKey:@"foo"] isEqualToString:@"bar"]);
error = error && !([(NSString *)[result objectForKey:@"bar"] isEqualToString:@"foo"]);
if (error)
STFail(@"NativeJsonSerializer could not deserialize dictionary");
}
- (void)test_serializerShouldDeserializeFloat {
NSNumber *result = [_serializer deserializeStringToObject:@"1.1"];
if (![result floatValue] == 1.1)
STFail(@"NativeJsonSerializer could not deserialize float");
}
- (void)test_serializerShouldDeserializeInteger {
NSNumber *result = [_serializer deserializeStringToObject:@"1"];
if (![result intValue] == 1)
STFail(@"NativeJsonSerializer could not deserialize integer");
}
- (void)test_serializerShouldDeserializeString {
NSString *result = [_serializer deserializeStringToObject:@"\"Foo Bar\""];
if (![result isEqualToString:@"Foo Bar"])
STFail(@"NativeJsonSerializer could not deserialize string");
}
- (void)test_serializerShouldSerializeArray {
NSMutableArray *data = [[NSMutableArray alloc] initWithCapacity:2];
[data addObject:@"foo"];
[data addObject:@"bar"];
NSString *result = [_serializer serializeObjectToString:data];
NSString *expectedResult = @"[\n \"foo\",\n \"bar\"\n]";
if (![result isEqualToString:expectedResult])
STFail(@"NativeJsonSerializer could not serialize array");
}
- (void)test_serializerShouldSerializeDictionary {
NSMutableDictionary *data = [[NSMutableDictionary alloc] init];
[data setObject:@"bar" forKey:@"foo"];
[data setObject:@"foo" forKey:@"bar"];
NSString *result = [_serializer serializeObjectToString:data];
NSString *expectedResult = @"{\n \"foo\" : \"bar\",\n \"bar\" : \"foo\"\n}";
if (![result isEqualToString:expectedResult])
STFail(@"NativeJsonSerializer could not serialize dictionary");
}
- (void)test_serializerShouldSerializeFloat {
NSString *result = [_serializer serializeObjectToString:[NSNumber numberWithFloat:1.1]];
if (![result isEqualToString:@"1.1"])
STFail(@"NativeJsonSerializer could not serialize float");
}
- (void)test_serializerShouldSerializeInteger {
NSString *result = [_serializer serializeObjectToString:[NSNumber numberWithInt:1]];
if (![result isEqualToString:@"1"])
STFail(@"NativeJsonSerializer could not serialize integer");
}
- (void)test_serializerShouldSerializeString {
NSString *result = [_serializer serializeObjectToString:@"Foo Bar"];
if (![result isEqualToString:@"\"Foo Bar\""])
STFail(@"NativeJsonSerializer could not serialize string");
}
@end
With this in place, handling JSON works like a dream in Objective-C.