Skip to content

Commit ef7b017

Browse files
author
Richard Piazza
committed
Better Collection de/serialization.
Added objectClassOfCollectionTypeForPropertyName: to CQLSerializableCustomizable protocol. This should allow for better automatic deserialization of arrays and sets.
1 parent 8f3bac0 commit ef7b017

File tree

5 files changed

+163
-104
lines changed

5 files changed

+163
-104
lines changed

CodeQuickKit-ObjC/CQKSerializable.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,4 +84,12 @@ typedef NSDictionary<NSString *, __kindof NSObject *> * _Nullable CQKSerializabl
8484
/// When used in the context of `CQKSerializable` the `dictionary` representation is returned.
8585
- (nullable __kindof NSObject *)serializedObjectForPropertyName:(nullable NSString *)propertyName withData:(nullable __kindof NSObject *)data;
8686

87+
/// objectClassOfCollectionTypeForPropertyName:
88+
/// ===========================================
89+
/// Specifyes the type of objects contained within a collection.
90+
/// Aids in deserializing array instances into appropriate `NSObject` subclasses.
91+
/// By default a singularized version of the provided propertyName will be used to
92+
/// identify the return class.
93+
- (nullable Class)objectClassOfCollectionTypeForPropertyName:(nullable NSString *)propertyName;
94+
8795
@end

CodeQuickKit-ObjC/CQKSerializableNSManagedObject.h

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -40,11 +40,4 @@
4040
/*! @abstract Calls the default initializer then passes the referenced dictionary to `CQKSerializable` updateWithJSON:. */
4141
- (nullable instancetype)initIntoManagedObjectContext:(nonnull NSManagedObjectContext *)context withJSON:(nullable NSString *)json;
4242

43-
/// classOfEntityForRelationshipWithAttributeName:
44-
/// ==============================================
45-
/// Allows for the specifying/overriding of the class for a given relationship.
46-
/// By default a singularized version of the provided attributeName will be used to identify the class.
47-
/// If no class is specified, NSNull class will be returned.
48-
- (nullable Class)classOfEntityForRelationshipWithAttributeName:(nullable NSString *)attributeName;
49-
5043
@end

CodeQuickKit-ObjC/CQKSerializableNSManagedObject.m

Lines changed: 48 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -77,49 +77,6 @@ - (instancetype)initIntoManagedObjectContext:(NSManagedObjectContext *)context w
7777
return self;
7878
}
7979

80-
- (Class)classOfEntityForRelationshipWithAttributeName:(NSString *)attributeName
81-
{
82-
if (attributeName == nil || [attributeName isEqualToString:@""]) {
83-
return [NSNull class];
84-
}
85-
86-
Class entityClass = NSClassFromString(attributeName);
87-
if (entityClass != nil) {
88-
return entityClass;
89-
}
90-
91-
NSMutableString *singular = [attributeName mutableCopy];
92-
if ([singular.lowercaseString hasSuffix:@"s"]) {
93-
[singular replaceCharactersInRange:NSMakeRange(singular.length - 1, 1) withString:@""];
94-
}
95-
96-
[singular replaceCharactersInRange:NSMakeRange(0, 1) withString:[singular substringToIndex:1].uppercaseString];
97-
98-
entityClass = NSClassFromString(singular);
99-
if (entityClass != nil) {
100-
return entityClass;
101-
}
102-
103-
NSBundle *bundle = [NSBundle bundleForClass:self.class];
104-
if (bundle.bundleDisplayName != nil) {
105-
NSString *moduleName = [NSString stringWithFormat:@"%@.%@", bundle.bundleDisplayName, singular];
106-
entityClass = NSClassFromString(moduleName);
107-
if (entityClass != nil) {
108-
return entityClass;
109-
}
110-
}
111-
112-
if (bundle.bundleName != nil) {
113-
NSString *moduleName = [NSString stringWithFormat:@"%@.%@", bundle.bundleName, singular];
114-
entityClass = NSClassFromString(moduleName);
115-
if (entityClass != nil) {
116-
return entityClass;
117-
}
118-
}
119-
120-
return [NSNull class];
121-
}
122-
12380
#pragma mark - CQKSerializable -
12481
- (instancetype)initWithDictionary:(NSDictionary<NSString *,__kindof NSObject *> *)dictionary
12582
{
@@ -331,7 +288,7 @@ - (NSObject *)initializedObjectForPropertyName:(NSString *)propertyName withData
331288
}
332289

333290
if ([propertyClass isSubclassOfClass:[NSSet class]]) {
334-
Class relationshipClass = [self classOfEntityForRelationshipWithAttributeName:propertyName];
291+
Class relationshipClass = [self objectClassOfCollectionTypeForPropertyName:propertyName];
335292
if ([relationshipClass isSubclassOfClass:[NSNull class]]) {
336293
return nil;
337294
}
@@ -341,9 +298,7 @@ - (NSObject *)initializedObjectForPropertyName:(NSString *)propertyName withData
341298
}
342299

343300
return [[relationshipClass alloc] initIntoManagedObjectContext:self.managedObjectContext withDictionary:data];
344-
}
345-
346-
if ([propertyClass isSubclassOfClass:[CQKSerializableNSManagedObject class]]) {
301+
} else if ([propertyClass isSubclassOfClass:[CQKSerializableNSManagedObject class]]) {
347302
return [[propertyClass alloc] initIntoManagedObjectContext:self.managedObjectContext withDictionary:data];
348303
}
349304

@@ -362,7 +317,7 @@ - (NSObject *)serializedObjectForPropertyName:(NSString *)propertyName withData:
362317
}
363318

364319
if ([[data class] isSubclassOfClass:[NSSet class]]) {
365-
Class relationshipClass = [self classOfEntityForRelationshipWithAttributeName:propertyName];
320+
Class relationshipClass = [self objectClassOfCollectionTypeForPropertyName:propertyName];
366321
if ([relationshipClass isSubclassOfClass:[NSNull class]]) {
367322
return nil;
368323
}
@@ -376,16 +331,57 @@ - (NSObject *)serializedObjectForPropertyName:(NSString *)propertyName withData:
376331
[serializedArray addObject:cqkObject.dictionary];
377332
}
378333
return serializedArray;
379-
}
380-
381-
if ([[data class] isSubclassOfClass:[CQKSerializableNSManagedObject class]]) {
334+
} else if ([[data class] isSubclassOfClass:[CQKSerializableNSManagedObject class]]) {
382335
return [(CQKSerializableNSManagedObject *)data dictionary];
383336
}
384337

385338
return [[CQKSerializableConfiguration sharedConfiguration] serializedObjectForPropertyName:propertyName withData:data];
386339
}
387340

388-
#pragma mark -
341+
- (Class)objectClassOfCollectionTypeForPropertyName:(NSString *)propertyName
342+
{
343+
if (propertyName == nil || [propertyName isEqualToString:@""]) {
344+
return [NSNull class];
345+
}
346+
347+
Class entityClass = NSClassFromString(propertyName);
348+
if (entityClass != nil) {
349+
return entityClass;
350+
}
351+
352+
NSMutableString *singular = [propertyName mutableCopy];
353+
if ([singular.lowercaseString hasSuffix:@"s"]) {
354+
[singular replaceCharactersInRange:NSMakeRange(singular.length - 1, 1) withString:@""];
355+
}
356+
357+
[singular replaceCharactersInRange:NSMakeRange(0, 1) withString:[singular substringToIndex:1].uppercaseString];
358+
359+
entityClass = NSClassFromString(singular);
360+
if (entityClass != nil) {
361+
return entityClass;
362+
}
363+
364+
NSBundle *bundle = [NSBundle bundleForClass:self.class];
365+
if (bundle.bundleDisplayName != nil) {
366+
NSString *moduleName = [NSString stringWithFormat:@"%@.%@", bundle.bundleDisplayName, singular];
367+
entityClass = NSClassFromString(moduleName);
368+
if (entityClass != nil) {
369+
return entityClass;
370+
}
371+
}
372+
373+
if (bundle.bundleName != nil) {
374+
NSString *moduleName = [NSString stringWithFormat:@"%@.%@", bundle.bundleName, singular];
375+
entityClass = NSClassFromString(moduleName);
376+
if (entityClass != nil) {
377+
return entityClass;
378+
}
379+
}
380+
381+
return [NSNull class];
382+
}
383+
384+
#pragma mark -
389385
- (void)setValueForPropertyName:(NSString *)propertyName withDictionary:(NSDictionary<NSString *,__kindof NSObject *> *)dictionary
390386
{
391387
if (propertyName == nil || dictionary == nil) {

CodeQuickKit-ObjC/CQKSerializableNSObject.m

Lines changed: 104 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -24,13 +24,15 @@
2424

2525
#import "CQKSerializableNSObject.h"
2626
#import "NSObject+CQKRuntime.h"
27+
#import "NSBundle+CQKBundle.h"
2728
#import "NSDateFormatter+CQKDateFormatter.h"
2829
#import "CQKSerializableConfiguration.h"
2930
#import "CQKLogger.h"
3031

3132
@interface CQKSerializableNSObject ()
3233
- (void)setValueForPropertyName:(NSString *)propertyName withDictionary:(NSDictionary *)dictionary;
3334
- (void)setValueForPropertyName:(NSString *)propertyName withArray:(NSArray *)array;
35+
- (void)setValueForPropertyName:(NSString *)propertyName withSet:(NSSet *)set;
3436
- (void)setValueForPropertyName:(NSString *)propertyName withObject:(id)object;
3537
@end
3638

@@ -166,44 +168,6 @@ - (void)updateWithDictionary:(NSDictionary<NSString *,__kindof NSObject *> *)dic
166168
}
167169
}];
168170

169-
// for (NSString *propertyName in properties) {
170-
// if (![self respondsToSelector:NSSelectorFromString(propertyName)]) {
171-
// NSString *message = [NSString stringWithFormat:@"%s[%@] Responds to selector = NO", __PRETTY_FUNCTION__, propertyName];
172-
// [CQKLogger log:CQKLoggerLevelVerbose message:message error:nil callingClass:[self class]];
173-
// continue;
174-
// }
175-
//
176-
// id valueObject = [self valueForKey:propertyName];
177-
// if (valueObject == nil) {
178-
// NSString *message = [NSString stringWithFormat:@"%s[%@] value = nil", __PRETTY_FUNCTION__, propertyName];
179-
// [CQKLogger log:CQKLoggerLevelVerbose message:message error:nil callingClass:[self class]];
180-
// continue;
181-
// }
182-
//
183-
// Class propertyClass = [NSObject classForPropertyName:propertyName ofClass:self.class];
184-
// NSString *serializedKey = [self serializedKeyForPropertyName:propertyName];
185-
//
186-
// @try {
187-
// if ([propertyClass isSubclassOfClass:[CQKSerializableNSObject class]]) {
188-
// [dictionary setObject:[(CQKSerializableNSObject *)valueObject dictionary] forKey:serializedKey];
189-
// } else if ([propertyClass isSubclassOfClass:[NSArray class]]) {
190-
// [dictionary setObject:[self serializedArrayForPropertyName:propertyName withArray:valueObject] forKey:serializedKey];
191-
// } else {
192-
// id serializedObject = [self serializedObjectForPropertyName:propertyName withData:valueObject];
193-
// if (serializedObject != nil) {
194-
// [dictionary setObject:serializedObject forKey:serializedKey];
195-
// }
196-
// }
197-
// }
198-
// @catch (NSException *exception) {
199-
// NSString *message = [NSString stringWithFormat:@"Failed to set value '%@' for key '%@': %@", valueObject, serializedKey, exception.reason];
200-
// [CQKLogger log:CQKLoggerLevelException message:message error:nil callingClass:[self class]];
201-
// }
202-
// @finally {
203-
//
204-
// }
205-
// }
206-
207171
return dictionary;
208172
}
209173

@@ -316,7 +280,25 @@ - (NSObject *)initializedObjectForPropertyName:(NSString *)propertyName withData
316280
return nil;
317281
}
318282

319-
if ([propertyClass isSubclassOfClass:[CQKSerializableNSObject class]]) {
283+
if ([propertyClass isSubclassOfClass:[NSArray class]]) {
284+
Class containingClass = [self objectClassOfCollectionTypeForPropertyName:propertyName];
285+
if ([containingClass isSubclassOfClass:[NSNull class]]) {
286+
return nil;
287+
}
288+
289+
if ([containingClass isSubclassOfClass:[CQKSerializableNSObject class]]) {
290+
return [[containingClass alloc] initWithDictionary:data];
291+
}
292+
} else if ([propertyClass isSubclassOfClass:[NSSet class]]) {
293+
Class containingClass = [self objectClassOfCollectionTypeForPropertyName:propertyName];
294+
if ([containingClass isSubclassOfClass:[NSNull class]]) {
295+
return nil;
296+
}
297+
298+
if ([containingClass isSubclassOfClass:[CQKSerializableNSObject class]]) {
299+
return [[containingClass alloc] initWithDictionary:data];
300+
}
301+
} else if ([propertyClass isSubclassOfClass:[CQKSerializableNSObject class]]) {
320302
return [[propertyClass alloc] initWithDictionary:data];
321303
} else if ([propertyClass isSubclassOfClass:[NSMutableDictionary class]]) {
322304
return [data mutableCopy];
@@ -351,15 +333,69 @@ - (NSObject *)serializedObjectForPropertyName:(NSString *)propertyName withData:
351333
}
352334
}
353335
return serializedArray;
354-
}
355-
356-
if ([[data class] isSubclassOfClass:[CQKSerializableNSObject class]]) {
336+
} else if ([[data class] isSubclassOfClass:[NSSet class]]) {
337+
NSMutableSet *serializedSet = [NSMutableSet set];
338+
for (id obj in (NSSet *)data) {
339+
if ([[obj class] isSubclassOfClass:[CQKSerializableNSObject class]]) {
340+
[serializedSet addObject:[(CQKSerializableNSObject *)obj dictionary]];
341+
} else if ([[obj class] isSubclassOfClass:[NSObject class]]) {
342+
NSObject *serializedObject = [self serializedObjectForPropertyName:propertyName withData:obj];
343+
if (serializedObject != nil) {
344+
[serializedSet addObject:serializedObject];
345+
}
346+
}
347+
}
348+
return serializedSet;
349+
} else if ([[data class] isSubclassOfClass:[CQKSerializableNSObject class]]) {
357350
return [(CQKSerializableNSObject *)data dictionary];
358351
}
359352

360353
return [[CQKSerializableConfiguration sharedConfiguration] serializedObjectForPropertyName:propertyName withData:data];
361354
}
362355

356+
- (Class)objectClassOfCollectionTypeForPropertyName:(NSString *)propertyName
357+
{
358+
if (propertyName == nil || [propertyName isEqualToString:@""]) {
359+
return [NSNull class];
360+
}
361+
362+
Class entityClass = NSClassFromString(propertyName);
363+
if (entityClass != nil) {
364+
return entityClass;
365+
}
366+
367+
NSMutableString *singular = [propertyName mutableCopy];
368+
if ([singular.lowercaseString hasSuffix:@"s"]) {
369+
[singular replaceCharactersInRange:NSMakeRange(singular.length - 1, 1) withString:@""];
370+
}
371+
372+
[singular replaceCharactersInRange:NSMakeRange(0, 1) withString:[singular substringToIndex:1].uppercaseString];
373+
374+
entityClass = NSClassFromString(singular);
375+
if (entityClass != nil) {
376+
return entityClass;
377+
}
378+
379+
NSBundle *bundle = [NSBundle bundleForClass:self.class];
380+
if (bundle.bundleDisplayName != nil) {
381+
NSString *moduleName = [NSString stringWithFormat:@"%@.%@", bundle.bundleDisplayName, singular];
382+
entityClass = NSClassFromString(moduleName);
383+
if (entityClass != nil) {
384+
return entityClass;
385+
}
386+
}
387+
388+
if (bundle.bundleName != nil) {
389+
NSString *moduleName = [NSString stringWithFormat:@"%@.%@", bundle.bundleName, singular];
390+
entityClass = NSClassFromString(moduleName);
391+
if (entityClass != nil) {
392+
return entityClass;
393+
}
394+
}
395+
396+
return [NSNull class];
397+
}
398+
363399
#pragma mark -
364400
- (void)setValueForPropertyName:(NSString *)propertyName withDictionary:(NSDictionary *)dictionary
365401
{
@@ -401,6 +437,32 @@ - (void)setValueForPropertyName:(NSString *)propertyName withArray:(NSArray *)ar
401437
}
402438
}
403439

440+
- (void)setValueForPropertyName:(NSString *)propertyName withSet:(NSSet *)set
441+
{
442+
if (propertyName == nil || set == nil) {
443+
return;
444+
}
445+
446+
Class propertyClass = [NSObject classForPropertyName:propertyName ofClass:self.class];
447+
if (propertyClass == [NSNull class]) {
448+
return;
449+
}
450+
451+
NSMutableSet *initializedSet = [NSMutableSet set];
452+
[set enumerateObjectsUsingBlock:^(id _Nonnull obj, BOOL * _Nonnull stop) {
453+
NSObject *initializedObject = [self initializedObjectForPropertyName:propertyName withData:obj];
454+
if (initializedObject != nil) {
455+
[initializedSet addObject:initializedObject];
456+
}
457+
}];
458+
459+
if ([propertyClass isSubclassOfClass:[NSMutableSet class]]) {
460+
[self setValue:initializedSet forKey:propertyName];
461+
} else {
462+
[self setValue:[NSSet setWithSet:initializedSet] forKey:propertyName];
463+
}
464+
}
465+
404466
- (void)setValueForPropertyName:(NSString *)propertyName withObject:(id)object
405467
{
406468
if (propertyName == nil || object == nil) {

CodeQuickKit/CodeQuickKitTests/CQKCoreDataTests.m

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -63,13 +63,13 @@ @implementation CQKCDPerson
6363
@dynamic name;
6464
@dynamic addresses;
6565

66-
- (Class)classOfEntityForRelationshipWithAttributeName:(NSString *)attributeName
66+
- (Class)objectClassOfCollectionTypeForPropertyName:(NSString *)propertyName
6767
{
68-
if ([attributeName isEqualToString:@"addresses"]) {
68+
if ([propertyName isEqualToString:@"addresses"]) {
6969
return [CQKCDAddress class];
7070
}
7171

72-
return [super classOfEntityForRelationshipWithAttributeName:attributeName];
72+
return [super objectClassOfCollectionTypeForPropertyName:propertyName];
7373
}
7474

7575
@end

0 commit comments

Comments
 (0)