Skip to content

Commit 19f1d99

Browse files
committed
https://github.com/icanzilb/JSONModel/issues/180
1 parent eac5c4b commit 19f1d99

4 files changed

Lines changed: 75 additions & 28 deletions

File tree

JSONModel/JSONModel/JSONModel.m

Lines changed: 25 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -181,16 +181,13 @@ -(id)initWithDictionary:(NSDictionary*)dict error:(NSError**)err
181181
return nil;
182182
}
183183

184-
//key mapping
185-
JSONKeyMapper* keyMapper = [self __keyMapper];
186-
187184
//check incoming data structure
188-
if (![self __doesDictionary:dict matchModelWithKeyMapper:keyMapper error:err]) {
185+
if (![self __doesDictionary:dict matchModelWithKeyMapper:self.__keyMapper error:err]) {
189186
return nil;
190187
}
191188

192189
//import the data from a dictionary
193-
if (![self __importDictionary:dict withKeyMapper:keyMapper validation:YES error:err]) {
190+
if (![self __importDictionary:dict withKeyMapper:self.__keyMapper validation:YES error:err]) {
194191
return nil;
195192
}
196193

@@ -206,10 +203,7 @@ -(id)initWithDictionary:(NSDictionary*)dict error:(NSError**)err
206203
-(JSONKeyMapper*)__keyMapper
207204
{
208205
//get the model key mapper
209-
JSONKeyMapper* keyMapper = objc_getAssociatedObject(self.class, &kMapperObjectKey);
210-
if (!keyMapper && globalKeyMapper) keyMapper = globalKeyMapper;
211-
212-
return keyMapper;
206+
return objc_getAssociatedObject(self.class, &kMapperObjectKey);
213207
}
214208

215209
-(BOOL)__doesDictionary:(NSDictionary*)dict matchModelWithKeyMapper:(JSONKeyMapper*)keyMapper error:(NSError**)err
@@ -220,16 +214,15 @@ -(BOOL)__doesDictionary:(NSDictionary*)dict matchModelWithKeyMapper:(JSONKeyMapp
220214
NSSet* incomingKeys = [NSSet setWithArray: incomingKeysArray];
221215

222216
//transform the key names, if neccessary
223-
if (keyMapper) {
217+
if (keyMapper || globalKeyMapper) {
224218

225219
NSMutableSet* transformedIncomingKeys = [NSMutableSet setWithCapacity: requiredProperties.count];
226220
NSString* transformedName = nil;
227221

228222
//loop over the required properties list
229223
for (JSONModelClassProperty* property in [self __properties__]) {
230224

231-
//get the mapped key path
232-
transformedName = keyMapper.modelToJSONKeyBlock(property.name);
225+
transformedName = (keyMapper||globalKeyMapper) ? [self __mapString:property.name withKeyMapper:keyMapper importing:YES] : property.name;
233226

234227
//chek if exists and if so, add to incoming keys
235228
id value;
@@ -269,15 +262,30 @@ -(BOOL)__doesDictionary:(NSDictionary*)dict matchModelWithKeyMapper:(JSONKeyMapp
269262
return YES;
270263
}
271264

265+
-(NSString*)__mapString:(NSString*)string withKeyMapper:(JSONKeyMapper*)keyMapper importing:(BOOL)importing
266+
{
267+
if (keyMapper) {
268+
//custom mapper
269+
NSString* mappedName = [keyMapper convertValue:string isImportingToModel:importing];
270+
if (globalKeyMapper && [mappedName isEqualToString: string]) {
271+
mappedName = [globalKeyMapper convertValue:string isImportingToModel:importing];
272+
}
273+
string = mappedName;
274+
} else if (globalKeyMapper) {
275+
//global keymapper
276+
string = [globalKeyMapper convertValue:string isImportingToModel:importing];
277+
}
278+
279+
return string;
280+
}
281+
272282
-(BOOL)__importDictionary:(NSDictionary*)dict withKeyMapper:(JSONKeyMapper*)keyMapper validation:(BOOL)validation error:(NSError**)err
273283
{
274284
//loop over the incoming keys and set self's properties
275285
for (JSONModelClassProperty* property in [self __properties__]) {
276286

277287
//convert key name ot model keys, if a mapper is provided
278-
NSString* jsonKeyPath = property.name;
279-
if (keyMapper) jsonKeyPath = keyMapper.modelToJSONKeyBlock( property.name );
280-
288+
NSString* jsonKeyPath = (keyMapper||globalKeyMapper) ? [self __mapString:property.name withKeyMapper:keyMapper importing:YES] : property.name;
281289
//JMLog(@"keyPath: %@", jsonKeyPath);
282290

283291
//general check for data type compliance
@@ -915,12 +923,6 @@ -(NSDictionary*)toDictionaryWithKeys:(NSArray*)propertyNames
915923

916924
id value;
917925

918-
//get the key mapper
919-
JSONKeyMapper* keyMapper = objc_getAssociatedObject(self.class, &kMapperObjectKey);
920-
921-
//if no custom mapper, check for a global mapper
922-
if (keyMapper==nil && globalKeyMapper!=nil) keyMapper = globalKeyMapper;
923-
924926
//loop over all properties
925927
for (JSONModelClassProperty* p in properties) {
926928

@@ -929,12 +931,9 @@ -(NSDictionary*)toDictionaryWithKeys:(NSArray*)propertyNames
929931
continue;
930932

931933
//fetch key and value
932-
NSString* keyPath = p.name;
934+
NSString* keyPath = (self.__keyMapper||globalKeyMapper) ? [self __mapString:p.name withKeyMapper:self.__keyMapper importing:YES] : p.name;
933935
value = [self valueForKey: p.name];
934936

935-
//convert the key name, if a key mapper exists
936-
if (keyMapper) keyPath = keyMapper.modelToJSONKeyBlock(keyPath);
937-
938937
//JMLog(@"toDictionary[%@]->[%@] = '%@'", p.name, keyPath, value);
939938

940939
if ([keyPath rangeOfString:@"."].location != NSNotFound) {
@@ -1240,7 +1239,7 @@ +(BOOL)propertyIsIgnored:(NSString *)propertyName
12401239
#pragma mark - working with incomplete models
12411240
-(void)mergeFromDictionary:(NSDictionary*)dict useKeyMapping:(BOOL)useKeyMapping
12421241
{
1243-
[self __importDictionary:dict withKeyMapper:(useKeyMapping)?[self __keyMapper]:nil validation:NO error:nil];
1242+
[self __importDictionary:dict withKeyMapper:(useKeyMapping)? self.__keyMapper:nil validation:NO error:nil];
12441243
}
12451244

12461245
#pragma mark - NSCopying, NSCoding

JSONModel/JSONModelTransformations/JSONKeyMapper.h

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,13 @@ typedef NSString* (^JSONModelKeyMapBlock)(NSString* keyName);
5757
/** Block, which takes in a property name and converts it to the corresponding JSON key name */
5858
@property (readonly, nonatomic) JSONModelKeyMapBlock modelToJSONKeyBlock;
5959

60+
/** Combined convertor method
61+
* @param value the source name
62+
* @param importing YES invokes JSONToModelKeyBlock, NO - modelToJSONKeyBlock
63+
* @return JSONKeyMapper instance
64+
*/
65+
-(NSString*)convertValue:(NSString*)value isImportingToModel:(BOOL)importing;
66+
6067
/** @name Creating a key mapper */
6168

6269
/**
@@ -84,5 +91,5 @@ typedef NSString* (^JSONModelKeyMapBlock)(NSString* keyName);
8491
*/
8592
+(instancetype)mapperFromUnderscoreCaseToCamelCase;
8693

87-
+ (instancetype)mapperFromUpperCaseToLowerCase;
94+
+(instancetype)mapperFromUpperCaseToLowerCase;
8895
@end

JSONModel/JSONModelTransformations/JSONKeyMapper.m

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,12 +88,16 @@ -(instancetype)initWithDictionary:(NSDictionary*)map
8888
NSString* result = [userToJSONMap valueForKeyPath: keyName];
8989
return result?result:keyName;
9090
};
91-
9291
}
9392

9493
return self;
9594
}
9695

96+
-(NSString*)convertValue:(NSString*)value isImportingToModel:(BOOL)importing
97+
{
98+
return !importing?_JSONToModelKeyBlock(value):_modelToJSONKeyBlock(value);
99+
}
100+
97101
+(instancetype)mapperFromUnderscoreCaseToCamelCase
98102
{
99103
JSONModelKeyMapBlock toModel = ^ NSString* (NSString* keyName) {

JSONModelDemoTests/UnitTests/KeyMappingTests.m

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ @interface TestModel: JSONModel
2020
@property (strong, nonatomic) NSString* text1;
2121
@property (strong, nonatomic) NSString<Optional>* text2;
2222

23+
@property (strong, nonatomic) NSString<Optional>* text3;
24+
2325
@end
2426
@implementation TestModel
2527

@@ -250,4 +252,39 @@ -(void)testMergingData
250252
[JSONModel setGlobalKeyMapper:nil];
251253
}
252254

255+
//https://github.com/icanzilb/JSONModel/issues/180
256+
-(void)testUsingBothGlobalAndCustomMappers
257+
{
258+
//input dictionary for TestModel
259+
NSDictionary* dict = @{
260+
@"texts": @{
261+
@"text1": @"TEST!!!",
262+
@"text2": @{@"value":@"MEST"},
263+
@"text3": @"Marin"
264+
}
265+
};
266+
267+
//test import via gloabl key mapper
268+
[JSONModel setGlobalKeyMapper:[[JSONKeyMapper alloc] initWithDictionary:@{
269+
@"texts.text3":@"text3"
270+
}]];
271+
272+
NSError* err = nil;
273+
TestModel* model = [[TestModel alloc] initWithDictionary:dict error:&err];
274+
275+
XCTAssertTrue(err==nil, @"Error creating TestModel: %@", [err localizedDescription]);
276+
XCTAssertTrue(model!=nil, @"TestModel instance is nil");
277+
278+
XCTAssertTrue([model.text3 isEqualToString:@"Marin"], @"text3 is not 'Marin'");
279+
280+
NSDictionary* toDict = [model toDictionary];
281+
282+
XCTAssertTrue([toDict[@"texts"][@"text3"] isEqualToString:@"Marin"], @"toDict.texts.text3 is not 'Marin'");
283+
284+
NSString* toString = [model toJSONString];
285+
XCTAssertTrue([toString rangeOfString:@"text3\":\"Marin"].location!=NSNotFound, @"model did not export text3 in string");
286+
287+
[JSONModel setGlobalKeyMapper:nil];
288+
}
289+
253290
@end

0 commit comments

Comments
 (0)