aryaxt / OCMapper

Objective-C library to easily map NSDictionary to model objects, works perfectly with Alamofire. ObjectMapper works similar to GSON
MIT License
347 stars 45 forks source link

Manual setting of mapping provider to return currentNode is needed to prevent empty fetched data from JSON #55

Closed haxpor closed 8 years ago

haxpor commented 8 years ago

I have 2 model classes

@interface PBQuiz : NSObject <OCMapperConfigurable>

@property (nonatomic, strong) NSString* name;
@property (nonatomic, strong) NSString* image;
@property (nonatomic, strong) NSNumber* weight;
@property (nonatomic, strong) NSArray<NSString*>* tags;
@property (nonatomic, strong) NSString* desc;
@property (nonatomic, strong) NSString* descriptionImage;
@property (nonatomic, strong) NSString* quizId;

@end

and

@interface PBQuizDetail : PBQuiz <OCMapperConfigurable>

@property (nonatomic, strong) NSDate* dateStart;
@property (nonatomic, strong) NSDate* dateExpire;
@property (nonatomic, strong) PBQuizType* type;
@property (nonatomic, strong) NSArray<PBQuizGrade*>* quizGrades;
@property (nonatomic, strong) NSNumber* status;
@property (nonatomic, strong) NSNumber* questionOrder;
@property (nonatomic, strong) NSNumber* deleted;
@property (nonatomic, strong) NSNumber* totalMaxScore;
@property (nonatomic, strong) NSNumber* totalQuestions;
@property (nonatomic, strong) NSNumber* questions;
@property (nonatomic, strong) NSNumber* totalScore;
@property (nonatomic, strong) NSDate* dateJoin;

@end

I have to manually add the following code

[mappingProvider mapFromDictionaryKey:@"grades" toPropertyKey:@"grades" forClass:[PBQuizDetail class] withTransformer:^id(id currentNode, id parentNode) {
        return currentNode;
    }];

just to return currentNode in order to receive value from JSON. If I didn't do this, I will always get 0 elements. Interesting thing also is that it's marked as JKArray type when I see it in debugger. I also use JSONKit. I'm not sure if OCMapper is conflict with JSONKit and cause this issue.

Note, OCMapperConfigurable is just protocol I use which has no effect of this issue.

haxpor commented 8 years ago

I just realize manually set mapping provider and return currentNode will get us NSDictionary as type.

I have remove JSONKit, and the problem still exists.

aryaxt commented 8 years ago

hmm you are mapping from a dictionary key named grades does that key exist in the data? you are mapping it to a property named "grades" on the object of type PBQuizDetail, but that class doesn't have a property named "grades".

What exactly are you trying to map?

If you are trying to map an array of grades to quizGrades you can do

[inCodeMappingProvider mapFromDictionaryKey:@"KEY_FROM_DICTIONARY" toPropertyKey:@"quizGrades" withObjectType:PBQuizGrade.class forClass:PBQuizDetail.class];
// replace KEY_FROM_DICTIONARY with the name of the key in your dictionary
haxpor commented 8 years ago

@aryaxt Thanks. I got it working by using your suggestion. Actually, what I want to do is not to map anything to anything. JSON data is there, but it didn't deserialize into model object. It's weird that I have to use your suggestion to manually tell model object class to map the field in which normally it will do that automatically.

That issue starts with

@property (nonatomic, strong) NSArray<PBQuizGrade*>* quizGrades in QuizDetail class.

then it also goes for NSArray property nested inside PBQuizGrade. It goes like this in chain.

I suspect something. In PBQuiz class, there's a JSON field description. I mapped it to property desc because it will be conflicting with description method. It works fine, but now I have PBQuizDetail class which extends PBQuiz, if I didn't map description to desc for PBQuizDetail class (not PBQuiz class). The crash will show as follows.

2016-08-02 09:32:50.377 xctest[28900:3636605] *** Terminating app due to uncaught exception 'NSUnknownKeyException', reason: '[<PBQuizDetail 0x7fc947724e90> setValue:forUndefinedKey:]: this class is not key value coding-compliant for the key description.'

So I have to do that to avoid crash. I suspect that this might be the root cause that I have to manually map those arrays.

haxpor commented 8 years ago

I think this is because the automatic mapping that tries to map singular noun class from plural noun in NSArray type of data. Thus it cannot find the proper class, and I have to manually tell it.

Could this be automatic process as I always provide the type of element something like this NSArray<MyObject*>*?

aryaxt commented 8 years ago

Could this be automatic process as I always provide the type of element something like this NSArray<MyObject>?

Unfortunately no it can't be done

The reason is objective c runtime-api doesn't provide info about the types of objects in an array, the way it works automatically in some cases is by using property names to try to find a matching class. in this case you don't have a matching property/class name and dictionary key. That's why it has to be done manually

What OCMapper does for trying to map arrays is to check for the key in dictionary. For example in this case your dictionary-key is quizGrades so OCMapper looks for a class named QuizGrades or QuizGrade, if the class exists it for mapping otherwise it'll ignore it

haxpor commented 8 years ago

Hey thanks so much much for explanation. It would be real useful but too bad the runtime doesn't support. Most of the time I have model class that has prefix naming, but definitely json field naming doesn't include prefix.

Ok, so for this case do you think it's better to close this issue or leave it as improvement later (not sure if there's a chance for it to support?).

aryaxt commented 8 years ago

You're welcome :) I think I'll close this since it's really not a supported feature of objective c

NOTE: This behavior is only for collections (array, set, etc), for non collection properties the type of objects can be determined at runtime, and they will be mapped automatically as long as dictionary key is named the same a property name

🍻