10

Anyone have idea how to serialize nested JSON based on NSObject class? There is a discussion to serialize simple JSON here , but it is not generic enough to cater complex nested JSON.

Imagine this is the result of JSON:

{ "accounting" : [{ "firstName" : "John",  
                    "lastName"  : "Doe",
                    "age"       : 23 },

                  { "firstName" : "Mary",  
                    "lastName"  : "Smith",
                    "age"       : 32 }
                              ],                            
  "sales"      : [{ "firstName" : "Sally", 
                    "lastName"  : "Green",
                    "age"       : 27 },

                  { "firstName" : "Jim",   
                    "lastName"  : "Galley",
                    "age"       : 41 }
                  ]}

From this class:

@interface Person : NSObject{}
@property (nonatomic, strong) NSString *firstName;
@property (nonatomic, strong) NSString *lastName;
@property (nonatomic, strong) NSNumber *age;
@end

@interface Department : NSObject{}
@property (nonatomic, strong) NSMutableArray *accounting; //contain Person class
@property (nonatomic, strong) NSMutableArray *sales; //contain Person class
@end

How to serialize/deserialize them based on class generically?

EDIT

Currently i'm able to generate payload like this based on any class:

NSMutableDictionary *Payload = [self serialize:objClass];

But it does not cater nested complex JSON. Anyone have better solution for this? This library for C# cater serialize/deserialze based on object class. I want to reproduce something the same based on NSObject

Community
  • 1
  • 1
Alphapico
  • 2,893
  • 2
  • 30
  • 29
  • One way to do it is not -- just go ahead and deal with NSDictionary objects instead of the custom classes. This often works better than you'd think. – Hot Licks Jan 08 '14 at 13:12
  • You can always add a method to your class `initWithJSONObject:error:` (via a category, for example). – CouchDeveloper Jan 08 '14 at 13:47
  • @CouchDeveloper - `initWithDictionary` is probably more general. (You can include the `error:` parm if it's merited.) – Hot Licks Jan 08 '14 at 16:49
  • @HotLicks I prefer to use more distinct names in Categories, possibly even using a prefix. But I agree, as a init method in a class `initWithDictionary` is a good choice. – CouchDeveloper Jan 08 '14 at 16:58
  • The thing is, if you get in the habit of using `initWithDictionary` you don't really need to worry about reading JSON into objects. It pretty much just works -- pass the dictionary to the init routine, it handles what it can, and creates subsidiary objects and calls their `initWithDictionary` methods in due course. So long as the JSON roughly matches the object structure you're good. – Hot Licks Jan 08 '14 at 19:26
  • (And you can have a superclass implement code to loop through the dictionary and do `setValue:forKey:` on the dictionary to handle trivial cases where JSON keys match property names with no data conversions. Just have the subclass remove entries that it must handle specially.) – Hot Licks Jan 08 '14 at 19:30
  • This link helps you.....[http://stackoverflow.com/a/29095726/3177007](http://stackoverflow.com/a/29095726/3177007) – Mohamed Jaleel Nazir Mar 17 '15 at 09:50

3 Answers3

12

Finally we can solve this problem easily using JSONModel. This is the best method so far. JSONModel is a library that generically serialize/deserialize your object based on Class. You can even use non-nsobject based for property like int, short and float. It can also cater nested-complex JSON.

1) Deserialize example. By referring to above example, in header file:

#import "JSONModel.h"

@interface Person : JSONModel 
@property (nonatomic, strong) NSString *firstName;
@property (nonatomic, strong) NSString *lastName;
@property (nonatomic, strong) NSNumber *age;
@end

@protocol Person;

@interface Department : JSONModel
@property (nonatomic, strong) NSMutableArray<Person> *accounting;
@property (nonatomic, strong) NSMutableArray<Person> *sales;
@end

in implementation file:

#import "JSONModelLib.h"
#import "myJSONClass.h"

NSString *responseJSON = /*from example*/;
Department *department = [[Department alloc] initWithString:responseJSON error:&err];
if (!err)
{
    for (Person *person in department.accounting) {

        NSLog(@"%@", person.firstName);
        NSLog(@"%@", person.lastName);
        NSLog(@"%@", person.age);         
    }

    for (Person *person in department.sales) {

        NSLog(@"%@", person.firstName);
        NSLog(@"%@", person.lastName);
        NSLog(@"%@", person.age);         
    }
}

2) Serialize Example. In implementation file:

#import "JSONModelLib.h"
#import "myJSONClass.h"

Department *department = [[Department alloc] init];

Person *personAcc1 = [[Person alloc] init];
personAcc1.firstName = @"Uee";
personAcc1.lastName = @"Bae";
personAcc1.age = [NSNumber numberWithInt:22];
[department.accounting addOject:personAcc1];

Person *personSales1 = [[Person alloc] init];
personSales1.firstName = @"Sara";
personSales1.lastName = @"Jung";
personSales1.age = [NSNumber numberWithInt:20];
[department.sales addOject:personSales1];

NSLog(@"%@", [department toJSONString]);

And this is NSLog result from Serialize example:

{ "accounting" : [{ "firstName" : "Uee",  
                    "lastName"  : "Bae",
                    "age"       : 22 }
                 ],                            
  "sales"      : [{ "firstName" : "Sara", 
                    "lastName"  : "Jung",
                    "age"       : 20 }
                  ]}
Alphapico
  • 2,893
  • 2
  • 30
  • 29
  • 1
    Since you know that models are going to be populated from json, you can include an `initWithDictionary:` method which leave the mapping to respective models. – Anupdas Jun 05 '13 at 10:08
  • Yes, `initWithDictionary` is an excellent approach to initialization in this case, and has the advantage that it can also be used in the general case where the object is being inited in response to "local" needs. Especially with the new `@{...}` notation it's easy to create a dictionary with all the parms in it (in the event that JSON doesn't hand you one ready-made). In addition, if you use the JSONSerialization option that gives you mutable objects, you can modify the incoming dictionary to, eg, change a key name, or add additional parms that may be missing from the JSON. – Hot Licks Jan 08 '14 at 16:48
1

You must know ahead of time what sort of object you will be deserializing. In this case, you're going to be deserializing to an NSDictionary that has two properties: "accounting" and "sales". Each of these properties will be an instance of NSArray. The arrays will have instances of NSDictionary.

Since you know what each of these objects really are, once you have deserialized the JSON into native objects you can create new instances of your classes out of the deserialized objects. For example:

JSONDecoder decoder = [[JSONDecoder alloc] init];
NSObject notJSON = [decoder objectWithData:jsonData];
// where jsonData is an NSData representation of your JSON
[decoder release];

Person person1 = (Person)[notJSON objectForKey:@"accounting"][0];

Given this example, you should be able to extrapolate to a more generic deserializer. That is, you'd want to loop through your data to create a deep copy of your "unknown" generic object to a "known" specific object.

James Sumners
  • 14,485
  • 10
  • 59
  • 77
  • 1
    I'm sorry this is not generic. See [this](http://stackoverflow.com/questions/10515015/ios-json-serialization-for-nsobject-based-classes) for reference – Alphapico Feb 19 '13 at 13:51
  • 1
    As I said, and as was said in your reference, it's not really possible to be totally generic in this. JSONKit is already decoding to generic native objects. It is up to you to know what they really are. If you can't require the JSON to tell you (through some property), then you have to expect a known format. – James Sumners Feb 19 '13 at 13:55
  • I have create quite a better solution for this, but it's still can't cater nested JSON. One layer-nested JSON no problem. With that I can pass any class I want to serialize/deserialize generically. Now I call anyone for helps if they have any idea how to do it for complex JSON – Alphapico Feb 19 '13 at 14:01
  • @MalcolmMashmallow - Why is "nested JSON" any more difficult? Just deal with one layer at a time. – Hot Licks Jan 08 '14 at 13:15
0

Maybe this one can help BWJSONMatcher. It helps you easily match a JSON string or JSON object up with your data model with one line of code.

...
NSString *jsonString = @"{your-json-string}";
YourValueObject *dataModel = [YourValueObject fromJSONString:jsonString];

NSDictionary *jsonObject = @{your-json-object};
YourValueObject *dataModel = [YourValueObject fromJSONObject:jsonObject];
...
YourValueObject *dataModel = instance-of-your-value-object;
NSString *jsonString = [dataModel toJSONString];
NSDictionary *jsonObject = [dataModel toJSONObject];
...
Burrows Wang
  • 249
  • 1
  • 7