"Bad property protocol declaration" Error with JSONModel library - ios

I have a simple JSON like this:
{
description = "Skill description",
languages = [
{
name = iOS,
skillLevel = 6
},
{
name = Android,
skillLevel = 4
}
]
}
and I need to create a model using JSON Model.
My model looks like this:
SkillModel.h
#import <Foundation/Foundation.h>
#import <JSONModel/JSONModel.h>
#protocol LanguageModel
#end
#interface SkillModel : JSONModel
// ----------------------------------------------------------------------------
//
// SkillModel
//
//
#property (strong, nonatomic) NSString *description;
//
#property (strong, nonatomic) NSArray<LanguageModel> *languages;
#end
// ----------------------------------------------------------------------------
//
// LanguageModel
//
#interface LanguageModel : JSONModel
//
#property (strong, nonatomic) NSString *name;
//
#property (strong, nonatomic) NSString *skillLevel;
#end
And the other code looks like this:
NSError *error;
NSDictionary *mobileSkillsDic = ...Dict data from json file....;
SkillModel *skillModel = [[SkillModel alloc] initWithDictionary:mobileSkillsDict error:&error];
What I'm doing wrong? I'm getting this error:
Terminating app due to uncaught exception 'Bad property protocol declaration, reason: LanguageModel is not allowed JSONModel property protocol, and not a JSONModel class.

Related

Mantle modelOfClass json parsing

I have the following Mantle object & want to convert the dictionary to Mantle object. but the conversion always returns empty MTLModel object.
Any ideas?
//ABC.h
#interface ABC : MTLModel<MTLJSONSerializing>
#property (nonatomic, strong) NSString *Identifier;
#property (nonatomic, strong) NSString *Name;
#property (nonatomic, strong) NSString *ImageURLString;
#property (nonatomic, strong) NSString *Desc;
#property (nonatomic, strong) NSString *lastUpdated;
#end
// ABC.m
#implementation ABC
+ (NSDictionary *)JSONKeyPathsByPropertyKey {
return #{
#"Identifier" : #"id",
#"Name" : #"name",
#"Desc" : #"desc",
#"ImageURLString" : #"image_url",
#"lastUpdated" : #"last_updated_at"
};
}
#end
Calling code:
ABC *abc = [MTLJSONAdapter modelOfClass:ABC.class fromJSONDictionary:tempDict error:NULL];
The JSON dictionary is:
{
desc = "asdadasdakjqewqwmsdnasdaksdasd";
id = adsasdasdasdasd;
"image_url" = "http://upload.wikimedia.org/wikipedia/commons/thumb/1/19/abc.JPG";
"last_updated_at" = "2015-07-25 04:22:39.851710";
name = asdasdqwe;
}
The result ABC object has no contents. Can't figure what I am missing here.
Any ideas???
Try with this snippet and debug the adapterError. May have relevant information about the conversion from NSDictionary to Mantle object
//Create Mantle object from NSDictionary using MTLJSONSerialization
NSError *adapterError;
MTLModel *user = [MTLJSONAdapter modelOfClass:MTLModel.class fromJSONDictionary:dictionary error:&adapterError];
if(adapterError) {
PrintError(adapterError)
return nil;
}
id = adsasdasdasdasd;
needs to be
id = "adsasdasdasdasd";

Mantle - Mapping Nested Data Structure

I have a slightly complex nested JSON object that I need to map to Objective-C class. One think I need to do is skip levels in JSON when mapping. Here is what I mean:
I want to map the following JSON Dictionary:
NSDictionary *JSONDictionary = #{
#"status" : #"PASSWORD_EXPIRED",
#"_embedded" : #{
#"user" : #{
#"id" : #"00ub0oNGTSWTBKOLGLNR",
#"profile" : #{
#"login" : #"isaac#example.org",
#"firstName" : #"Isaac",
#"lastName" : #"Brock",
#"locale" : #"en_US",
#"timeZone" : #"America/Los_Angeles"
}
}
}
};
to
#interface Authentication : MTLModel <MTLJSONSerializing>
#property (strong, nonatomic) NSDate* expiresAt;
#property (strong, nonatomic) NSString* status;
#property (strong, nonatomic) NSString* relayState;
#property (strong, nonatomic) NSString* sessionToken;
#property (strong, nonatomic) NSString* stateToken;
//Embeded Resources
#property (strong, nonatomic) User* user;
#end
As you can see I am not creating an object for "_embeded". I am skipping it and mapping user to a User object.
Can this be achieved? If so How?
I have tried this in the implementation but it did not work:
#implementation Authentication
+ (NSDictionary*)JSONKeyPathsByPropertyKey {
return #{
#"expiresAt" : #"expiresAt",
#"status" : #"status",
#"sessionToken" : #"sessionToken",
#"stateToken" : #"stateToken",
#"relayState" : #"relayState",
#"user" : #"_embeded/user",
};
}
+ (NSValueTransformer *)userJSONTransformer {
return [NSValueTransformer mtl_JSONDictionaryTransformerWithModelClass:User.class];
}
#end
I used to do it with the following code:
+ (NSValueTransformer *)userJSONTransformer {
return [MTLValueTransformer reversibleTransformerWithForwardBlock:^id(NSDictionary *userDict) {
return [MTLJSONAdapter modelOfClass:[User class] fromJSONDictionary:userDict error:nil];
} reverseBlock:^id(User *user) {
return [MTLJSONAdapter JSONDictionaryFromModel:user];
}];
}
And also in +JSONKeyPathsByPropertyKey I think it should be #"user" : #"_embeded.user"

MOTIS Object Mapping, With NSDictionary with values NSArray how can I specify type of array elements?

I have the json
{"Types":{
"food":[{"cve":"1","description":"Pizza"},{"cve":"2","description":"Restaurant"},{"cve":"3","description":"Cafe"}],
"Health":[{"cve":"3","description":"Pharmacy"},{"cve":"4","description":"Hospital"}]
} }
Types.h
#import <Foundation/Foundation.h>
#interface Types: NSObject
#property (nonatomic, copy) NSDictionary *types;
#end
Types.m
#import "Types.h"
#import <Motis/Motis.h>
#import "SubTipo.h"
#implementation Types
+ (NSDictionary*)mts_mapping
{
return #{#"types": mts_key(types),};
}
#end
Subtype.h
#import <Foundation/Foundation.h>
#interface Subtype: NSObject
#property (nonatomic, assign) int cve;
#property (nonatomic, copy) NSString *description;
#end
Subtype.m
#import "Subtype.h"
#import <Motis/Motis.h>
#implementation Subtype
+ (NSDictionary*)mts_mapping
{
return #{#"cve": mts_key(cve),
#"description": mts_key(description),
};
}
#end
I deserialize with
Types * values=[[Types alloc]init];
NSDictionary * jsonObject = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableContainers error:&error];
[values mts_setValuesForKeysWithDictionary:jsonObject ];
I get NSDictionary with NSArray of NSDictionary
but I need NSDictionary with NSArray of Subtypes
I try with
+ (NSDictionary*)mts_arrayClassMapping
{
return #{mts_key(types): Subtype.class};
}
but wasn't successful
How can I get these with Motis
As far as I see, your Types object is not properly defined. If you have an attribute of type NSDictionary* and the JSON received is a Dictionary, Motis won't perform any automatic conversion as the types already match (you are receiving a dictionary and your attribute is of type NSDictionary).
Therefore, you must implement your Type object following your JSON structure. This means that your Type object must have two properties of type array, one for food and one for health. Then, using the method +mts_arrayClassMapping you can specify the content type of the arrays to Subtype.
Here the implementation:
// ***** Type.h file ***** //
#interface Type: NSObject
#property (nonatomic, strong) NSArray *food;
#property (nonatomic, strong) NSArray *health;
#end
// ***** Type.m file ***** //
#implementation Type
+ (NSDictionary*)mts_mapping
{
return #{#"food": mts_key(food),
#"Health": mts_key(health),
};
}
+ (NSDictionary*)mts_arrayClassMapping
{
return #{mts_key(food): Subtype.class,
mts_key(health): Subtype.class,
};
}
#end
Regarding the implementation of Subtype, yours is already correct. However, you should not use the property name description as it is already being used by NSObject:
// ***** Subtype.h file ***** //
#interface Subtype: NSObject
#property (nonatomic, assign) NSInteger cve;
#property (nonatomic, copy) NSString *theDescription;
#end
// ***** Subtypes.m file ***** //
#implementation Subtype
+ (NSDictionary*)mts_mapping
{
return #{#"cve": mts_key(cve),
#"description": mts_key(theDescription),
};
}
#end
Finally, as you list above, you can map your JSON, but first you will have to extract the "dictionary" for key Types, which you will map to your "Type" model object.
// Get the json data
NSDictionary * jsonObject = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableContainers error:&error];
// Extract the JSON dictionary of types.
NSDictionary *jsonType = [jsonObject objectForKey:#"Types"];
// Create a Type object
Type *type = [[Type alloc] init];
// Map JSON contents to the type object with Motis
[type mts_setValuesForKeysWithDictionary:jsonType];
Hoping this fixes your issue.

parse json using JsonModel

Facing problem while getting a array of json.
I am using JsonModel library for json handelling.
My json data is:
[
{
"TSCard_id": 100,
"TSCardBackend_id": "TSu1t1",
"TSCard_date": "10/11/2013",
"TSCard_resource_id": "",
"TSCard_resource_name": "",
"TSCard_job_id": "JOB_M2156 ",
"TSCard_job_desc": "BAGARIA MILLIPORE - BOM ",
"TSCard_activity_id": "B03",
"TSCard_activity_desc": "Closing",
"TSCard_hours": "4.0",
"TSCard_chargeableflag": "0",
"TSCard_desc": "Description:",
"TSCard_syncflag": "0",
"TSCard_flag": "",
"user_id": "1"
},
{
"TSCard_id": 101,
"TSCardBackend_id": "TSu1t2",
"TSCard_date": "12/11/2013",
"TSCard_resource_id": "",
"TSCard_resource_name": "",
"TSCard_job_id": "JOB_B0002",
"TSCard_job_desc": "ABB LIMITED - BLR ",
"TSCard_activity_id": "NB01",
"TSCard_activity_desc": "Admin ",
"TSCard_hours": "5.0",
"TSCard_chargeableflag": "1",
"TSCard_desc": "Description:",
"TSCard_syncflag": "0",
"TSCard_flag": "",
"user_id": "1"
}
]
Above json has 2 objects of type TSCard class
//TSCard.h
#import <Foundation/Foundation.h>
#import <JSONModel.h>
#protocol TSCard #end
#interface TSCard : JSONModel
#property (nonatomic, retain) NSString *TSCard_id;
#property (nonatomic, retain) NSString *TSCardBackend_id;
#property (nonatomic, retain) NSString *TSCard_date;
#property (nonatomic, retain) NSString *TSCard_resource_id;
#property (nonatomic, retain) NSString *TSCard_resource_name;
#property (nonatomic, retain) NSString *TSCard_job_id;
#property (nonatomic, retain) NSString *TSCard_job_desc;
#property (nonatomic, retain) NSString *TSCard_activity_id;
#property (nonatomic, retain) NSString *TSCard_activity_desc;
#property (nonatomic, retain) NSString *TSCard_hours;
#property (nonatomic, retain) NSString *TSCard_chargeableflag;
#property (nonatomic, retain) NSString *TSCard_desc;
#property (nonatomic, retain) NSString *TSCard_syncflag;
#property (nonatomic, retain) NSString *TSCard_flag;
#property (nonatomic, retain) NSString *user_id;
#end
And I am making a class for array of type TSCard
//GetCardData.h
#import <JSONModel.h>
#import "TSCard.h"
#interface GetCardData : JSONModel
#property(strong,nonatomic)NSArray<TSCard>* myDataArray;
#end
then I parsing json as below:
NSError* err = nil;
GetCardData *c = [[GetCardData alloc] initWithString:jsonString error:&err];
NSLog(#"%d",c.myDataArray.count);
NSLog Error:
Error Domain=JSONModelErrorDomain Code=1 "Invalid JSON data: Attempt to initialize JSONModel object using initWithDictionary:error: but the dictionary parameter was not an 'NSDictionary'." UserInfo=0x8265490 {NSLocalizedDescription=Invalid JSON data: Attempt to initialize JSONModel object using initWithDictionary:error: but the dictionary parameter was not an 'NSDictionary'.}
I can't find where I am wrong...can anybody suggest the right way of using JsonModel and parsing a jsonString to array of TSCard objects correctly.
Try using following code for parsing your json and getting array
NSMutableArray *yourArray = [TSCard arrayOfModelsFromDictionaries:jsonString];
Than create and assign
GetCardData *objCardData = [[GetCardData alloc] init];
objCardData.myDataArray = yourArray;
or you need to change your JSONString as follow
{
"myDataArray": [
{
"TSCard_id": 100,
"TSCardBackend_id": "TSu1t1",
"TSCard_date": "10/11/2013",
"TSCard_resource_id": "",
"TSCard_resource_name": "",
"TSCard_job_id": "JOB_M2156 ",
"TSCard_job_desc": "BAGARIA MILLIPORE - BOM ",
"TSCard_activity_id": "B03",
"TSCard_activity_desc": "Closing",
"TSCard_hours": "4.0",
"TSCard_chargeableflag": "0",
"TSCard_desc": "Description:",
"TSCard_syncflag": "0",
"TSCard_flag": "",
"user_id": "1"
},
{
"TSCard_id": 101,
"TSCardBackend_id": "TSu1t2",
"TSCard_date": "12/11/2013",
"TSCard_resource_id": "",
"TSCard_resource_name": "",
"TSCard_job_id": "JOB_B0002",
"TSCard_job_desc": "ABB LIMITED - BLR ",
"TSCard_activity_id": "NB01",
"TSCard_activity_desc": "Admin ",
"TSCard_hours": "5.0",
"TSCard_chargeableflag": "1",
"TSCard_desc": "Description:",
"TSCard_syncflag": "0",
"TSCard_flag": "",
"user_id": "1"
}
]
}
Than your code will definitely work. Tell me if need more help.
For your problem, I personally recommend BWJSONMatcher. You don't have to do anything else or declare any protocol apart from your data model:
#interface TSCard : NSObject
#property (nonatomic, strong) NSString *TSCard_id;
#property (nonatomic, strong) NSString *TSCardBackend_id;
#property (nonatomic, strong) NSString *TSCard_date;
#property (nonatomic, strong) NSString *TSCard_resource_id;
#property (nonatomic, strong) NSString *TSCard_resource_name;
#property (nonatomic, strong) NSString *TSCard_job_id;
#property (nonatomic, strong) NSString *TSCard_job_desc;
#property (nonatomic, strong) NSString *TSCard_activity_id;
#property (nonatomic, strong) NSString *TSCard_activity_desc;
#property (nonatomic, strong) NSString *TSCard_hours;
#property (nonatomic, strong) NSString *TSCard_chargeableflag;
#property (nonatomic, strong) NSString *TSCard_desc;
#property (nonatomic, strong) NSString *TSCard_syncflag;
#property (nonatomic, strong) NSString *TSCard_flag;
#property (nonatomic, strong) NSString *user_id;
#end
You can then get your array with one neatly short line:
NSArray *dataArray = [BWJSONMatcher matchJSON:jsonString withClass:[TSCard class]];
You can using this method in JSONModel
NSError *error;
NSMutableArray <TSCard *> *yourArray = [TSCard arrayOfModelsFromString:strData error:&error];
and do your logic.

[MyClassName copyWithZone:]: unrecognized selector sent to instance?

I just implemented my class
#interface ExampleNestedTablesViewController ()
{
NSMutableArray *projectModelArray;
NSMutableDictionary *sectionContentDictionary;
}
- (void)viewDidLoad
{
[super viewDidLoad];
ProjectModel *project1 = [[ProjectModel alloc] init];
project1.projectName = #"Project 1";
ProjectModel *project2 = [[ProjectModel alloc] init];
project2.projectName = #"Project 2";
if (!projectModelArray)
{
projectModelArray = [NSMutableArray arrayWithObjects:project1, project2, nil];
}
if (!sectionContentDictionary)
{
sectionContentDictionary = [[NSMutableDictionary alloc] init];
NSMutableArray *array1 = [NSMutableArray arrayWithObjects:#"Task 1", #"Task 2", nil];
[sectionContentDictionary setValue:array1 forKey:[projectModelArray objectAtIndex:0]]; // **this line crashed**.
}
}
Here is my Project Model
#interface ProjectModel : NSObject
typedef enum
{
ProjectWorking = 0,
ProjectDelayed,
ProjectSuspended,
} ProjectStatus;
#property (nonatomic, assign) NSInteger idProject;
#property (nonatomic, strong) NSString* projectName;
#property (nonatomic, strong) NSMutableArray* listStaff;
#property (nonatomic, strong) NSTimer* projectTimer;
#property (nonatomic, assign) ProjectStatus projectStatus;
#property (nonatomic, strong) NSMutableArray* listTask;
#property (nonatomic, assign) NSInteger limitPurchase;
#property (nonatomic, strong) NSDate* limitTime;
#end
And the output is:
SDNestedTablesExample[1027:c07] -[ProjectModel copyWithZone:]: unrecognized selector sent to instance 0x7562920.
I didn't know which problem. Can you help me ?
Look at the docs for NSMutableDictionary setObject:forKey: (note you should use setObject:forKey:, not setValue:forKey:). Notice the expected type for the key. It must be of type id<NSCopying>. This means the key must conform to the NSCopying protocol.
Since your keys are of type ProjectModel, the error is complaining since your ProjectModel class doesn't implement the required method of the NSCopying protocol - copyWithZone:.
Are you sure you want to use a ProjectModel object as the key? Doing so also means you need a sane implementation of the isEqual: and hash methods, in addition to copyWithZone.
The solution is to update your ProjectModel class so it conforms to the NSCopying protocol and implements the copyWithZone: method. And also properly implement the isEqual: and hash methods. Or change the key to be just the idProject property (properly wrapped as an NSNumber).

Resources