objective-capimodeljsonmodel

Why is my JSONModel Class being treated as a NSDictionary?


This is my JSON fetched from the API:

 {
categories =     (
            {
        icon =             {
            prefix =     "https://ss3.4sqi.net/img/categories_v2/food/italian_";
            suffix = ".png";
        };
        id = 4bf58dd8d48988d110941735;
        name = "Italian Restaurant";
        pluralName = "Italian Restaurants";
        primary = 1;
        shortName = Italian;
    }
);
hasPerk = 0;
id = 4b5bed2ef964a520971d29e3;
location =     {
    address = "HS-5";
    cc = IN;
    city = "New Delhi";
    country = India;
    crossStreet = "Kailash Colony Market";
    distance = 4061;
    formattedAddress =         (
        "HS-5 (Kailash Colony Market)",
        "New Delhi 110024",
        Delhi,
        India
    );
    labeledLatLngs =         (
                    {
            label = display;
            lat = "28.55272788981442";
            lng = "77.24192322690806";
        }
    );
    lat = "28.55272788981442";
    lng = "77.24192322690806";
    postalCode = 110024;
    state = Delhi;
};
name = "The Big Chill Cafe";
referralId = "v-1546605733";
} 

And here is my model class:

@interface LocationModel : JSONModel

@property (nonatomic) NSInteger distance;
@property (nonatomic) NSInteger postalCode;
@property (nonatomic) NSString *address;
@property (nonatomic) NSString *cc;
@property (nonatomic) NSString *city;
@property (nonatomic) NSString *country;
@property (nonatomic) NSString *crossStreet;
@property (nonatomic) NSString *lat;
@property (nonatomic) NSString *lng;
@property (nonatomic) NSString *state;
@property (nonatomic) NSArray *formattedAddress;
@property (nonatomic) NSArray *labeledLatLngs;
@end


@protocol VenueModel;

@interface VenueModel : JSONModel

@property (nonatomic) NSArray *categories;
@property (nonatomic) BOOL hasPerk;
@property (nonatomic) NSString *id;
@property (nonatomic) LocationModel *location;
@property (nonatomic) NSString *name;
@property (nonatomic) NSString *referralId;
@end

@interface Restaurant : JSONModel
@property (nonatomic) NSInteger confident;
@property (nonatomic) NSArray <VenueModel*> *venues;
@end

Now, I am trying to get the values like this:

restaurant = [[Restaurant alloc] initWithDictionary

 [NSJSONSerialization JSONObjectWithData:JSON            
options:NSJSONReadingAllowFragments error:nil][@"response"] error:&error];

VenueModel *obj = [restaurant.venues firstObject];
LocationModel *locationObj = obj.location;

The values are coming fine till the VenueModel object, after that when I am trying to access the LocationModel object, i.e

LocationModel *locationObj = obj.location

, its throwing the following error:

Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[__NSDictionaryI location]: unrecognized selector sent to instance 0x600001b82880

Somebody please help me out, I need the location object.


Solution

  • NSJSONSerialization will produce NSDictionaries, NSArrays, NSStrings, NSNumbers and a few other basic types from a JSON string.

    Your idea to initialize a custom object ([[Restaurant alloc] initWithDictionary...) is the right idea, but that idea must be applied to nested structures as well. So, for example, the Restaurant initializer must apply that idea to its venues array.

    Otherwise, you'll get the crash you're getting, treating one of the venue objects as if its a VenueModel instead of what it really is (an NSDictionary).

    You didn't post initWithDictionary, but it needs to look something like this, especially the part where the objects it contains are initialized...

    - (id)initWithDictionary:(NSDictionary *)dictionary {
        self = [super init];
        // ...
        self.venues = [NSMutableArray array];
        if (self) {
            for (NSDictionary *venueDictionary in dictionary[@"venues"]) {
                VenueModel *venue = [[VenueModel alloc] initWithDictionary:venueDictionary];
                [self.venues addObject:venue];
            }
        }
        // and so on, for every other object type nested in dictionary
        return self;
    }
    

    Moreover, as you can see, VenueModel must also follow this practice, turning the location data into a LocationModel, and so on...