How to serialize a custom object in NSDictionary using JSON?

5.2k Views Asked by At

I have the following code:

NSDictionary *dict = [[NSDictionary alloc] initWithObjectsAndKeys:@"one", @"oneKey", 
@"two", @"twoKey", customObject, @"customObjectKey", nil];
if([NSJSONSerialization isValidJSONObject:dict])
{
    NSLog(@"Went through.");
}

It'll go through if the objects were NSString's, but once I added the customObject into the dictionary, it's no longer valid. How can I fix that? Any help is much appreciated. Thanks in advance!

6

There are 6 best solutions below

2
On BEST ANSWER

You would require to use NSCoding protocol to serialize a custom object.

Implement the NSCoding protocol in .h file and implement the following methods in .m file of your custom class.

- (id)initWithCoder:(NSCoder *)aDecoder
{
   if(self = [super init]) // this needs to be [super initWithCoder:aDecoder] if the superclass implements NSCoding
   {
      aString = [[aDecoder decodeObjectForKey:@"aStringkey"] retain];
      anotherString = [[aDecoder decodeObjectForKey:@"anotherStringkey"] retain];
   }
   return self;
}

and

- (void)encodeWithCoder:(NSCoder *)encoder
{
   // add [super encodeWithCoder:encoder] if the superclass implements NSCoding
   [encoder encodeObject:aString forKey:@"aStringkey"];
   [encoder encodeObject:anotherString forKey:@"anotherStringkey"];
}
1
On

You can't set a custom Object to a key of Dictionary. Get sure, what your keys is NSStrings

Text from Foundatoin.framework at func +(BOOL) isValidJSONObject:(id)obj;

- All dictionary keys are NSStrings
0
On

The simple truth is: You can't. Read the documentation of NSJSONSerialization, which doesn't list NSCoding. :(

1
On

CAUSE:This is because "customObject" is not: NSArray, NSDictionary nor NSString. Most probably will be a user defined class, this is the contract for isValidJSONOperation:

/* Returns YES if the given object can be converted to JSON data, NO otherwise. The object must have the following properties:
    - Top level object is an NSArray or NSDictionary
    - All objects are NSString, NSNumber, NSArray, NSDictionary, or NSNull
    - All dictionary keys are NSStrings
    - NSNumbers are not NaN or infinity
 Other rules may apply. Calling this method or attempting a conversion are the definitive ways to tell if a given object can be converted to JSON data.
 */
+ (BOOL)isValidJSONObject:(id)obj;

POSSIBLE SOLUTION 1:Add into your customObject class another attribute of type NSDictonary called by example dictionary which hold the attributes to add in the JSON.

NSDictionary *dict = [[NSDictionary alloc] initWithObjectsAndKeys:@"one", @"oneKey", 
@"two", @"twoKey", customObject.dictionary, @"customObjectKey", nil];

POSSIBLE SOLUTION 2: Create a new dictionary with the attributes (and values) that you want to add in the JSON from customObject.

1
On

Keys are strings, but the values can be any object including NSNull.

1
On

"How to serialize a custom object in NSDictionary using JSON in XCode?"

Chalk up another reason why I loathe XCode, and wish someone would drag it from the 1990s.

Let's go through an example of how we're expected to serialize a custom object.

Supposing you had a very simple UserRecord class, with a .h file like this:

@interface UserRecord : NSObject

@property(nonatomic) int UserID;
@property(nonatomic, strong) NSString* FirstName;
@property(nonatomic, strong) NSString* LastName;
@property(nonatomic) int Age;

@end

And an .m like this:

@implementation UserRecord

@synthesize UserID;
@synthesize FirstName;
@synthesize LastName;
@synthesize Age;

@end

If you tried to create a UserRecord object, and serialize it using the NSJSONSerialization class..

UserRecord* sampleRecord = [[UserRecord alloc] init];
sampleRecord.UserID = 13;
sampleRecord.FirstName = @"Mike";
sampleRecord.LastName = @"Gledhill";
sampleRecord.Age = 82;

NSError* error = nil;
NSData* jsonData2 = [NSJSONSerialization dataWithJSONObject:sampleRecord options:NSJSONWritingPrettyPrinted error:&error];

..it'd laugh at you, throw an exception and crash your application:

*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '*** +[NSJSONSerialization dataWithJSONObject:options:error:]: Invalid top-level type in JSON write'

One way to get around this farce is to add a function to your NSObject to convert your data into an NSDictionary, and then serialize that.

Here's my new .m file for my class:

@implementation UserRecord

@synthesize UserID;
@synthesize FirstName;
@synthesize LastName;
@synthesize Age;

-(NSDictionary*)fetchInDictionaryForm
{
    NSMutableDictionary* dict = [[NSMutableDictionary alloc] init];

    [dict setObject:[NSNumber numberWithInt:UserID] forKey:@"UserID"];
    [dict setObject:FirstName forKey:@"FirstName"];
    [dict setObject:LastName forKey:@"LastName"];
    [dict setObject:[NSNumber numberWithInt:Age] forKey:@"Age"];

    return dict;
}

@end

Actually, you could create that NSDictionary value in one go, if you wanted to:

-(NSDictionary*)fetchInDictionaryForm
{
    NSDictionary *dict = [NSDictionary dictionaryWithObjectsAndKeys:
                         [NSNumber numberWithInt:UserID], @"UserID",
                         FirstName, @"FirstName",
                         LastName,@"LastName",
                         [NSNumber numberWithInt:Age], @"Age",
                         nil];
    return dict;
}

Once you've done this, you can get NSJSONSerialization to serialize the NSDictionary version of your object:

UserRecord* sampleRecord = [[UserRecord alloc] init];
sampleRecord.UserID = 13;
sampleRecord.FirstName = @"Mike";
sampleRecord.LastName = @"Gledhill";
sampleRecord.Age = 82;


NSDictionary* dictionary = [sampleRecord fetchInDictionaryForm];
if ([NSJSONSerialization isValidJSONObject:dictionary])
{
    NSError* error = nil;
    NSData* jsonData = [NSJSONSerialization dataWithJSONObject:dictionary options:NSJSONWritingPrettyPrinted error:&error];
    NSString* jsonString = [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding];

    NSLog(@"%@", jsonString);
}

And this will produce the JSON output we wanted:

{
  "UserID" : 13,
  "FirstName" : "Mike",
  "LastName" : "Gledhill",
  "Age" : 82
}

It's shocking. Even in 2015, Apple's SDK isn't capable of serializing a simple set of ints and NSStrings into JSON.

Hope this helps other XCode victims.