// // Understruck.m // YHScrollView_Test // // Created by Jim on 2021/9/4. // #import "Understruck.h" #import <objc/runtime.h> @implementation Understruck static const char *getPropertyType(objc_property_t property) { const char *attributes = property_getAttributes(property); //printf("attributes=%s\n", attributes); char buffer[1 + strlen(attributes)]; strcpy(buffer, attributes); char *state = buffer, *attribute; while ((attribute = strsep(&state, ",")) != NULL) { if (attribute[0] == 'T' && attribute[1] != '@') { // it's a C primitive type: /* if you want a list of what will be returned for these primitives, search online for "objective-c" "Property Attribute Description Examples" apple docs list plenty of examples of what you get for int "i", long "l", unsigned "I", struct, etc. */ NSString *name = [[NSString alloc] initWithBytes:attribute + 1 length:strlen(attribute) - 1 encoding:NSASCIIStringEncoding]; return (const char *)[name cStringUsingEncoding:NSASCIIStringEncoding]; } else if (attribute[0] == 'T' && attribute[1] == '@' && strlen(attribute) == 2) { // it's an ObjC id type: return "id"; } else if (attribute[0] == 'T' && attribute[1] == '@') { // it's another ObjC object type: NSString *name = [[NSString alloc] initWithBytes:attribute + 3 length:strlen(attribute) - 4 encoding:NSASCIIStringEncoding]; return (const char *)[name cStringUsingEncoding:NSASCIIStringEncoding]; } } return ""; } #pragma mark - Get properties for a class + (NSDictionary *)propertiesForClass:(Class)cls { if (cls == NULL) { return nil; } NSMutableDictionary *results = [[NSMutableDictionary alloc] init]; unsigned int outCount, i; objc_property_t *properties = class_copyPropertyList(cls, &outCount); for (i = 0; i < outCount; i++) { objc_property_t property = properties[i]; const char *propName = property_getName(property); if(propName) { const char *propType = getPropertyType(property); NSString *propertyName = [NSString stringWithUTF8String:propName]; NSString *propertyType = [NSString stringWithUTF8String:propType]; [results setObject:propertyType forKey:propertyName]; } } free(properties); // returning a copy here to make sure the dictionary is immutable return [NSDictionary dictionaryWithDictionary:results]; } +(id)populateObject:(id)obj fromDictionary:(NSDictionary *)dict exclude:(NSArray *)excludeArray { if (obj == nil) { return nil; } Class cls = [obj class]; NSDictionary* properties = [Understruck propertiesForClass:cls]; for (id key in dict) { if ([key isKindOfClass:[NSString class]] == NO) { //NSLog(@"TDUtils: key must be NSString. Received key %@", key); continue; } // If key is not inside the object properties, skip it if ([properties objectForKey:key] == nil) { if (![key isEqualToString:@"id"]) { continue; } // NSLog(@"TDUtils: key %@ is not existed in class %@", key, NSStringFromClass(cls)); } // If key inside excludeArray, skip it if (excludeArray && [excludeArray indexOfObject:key] != NSNotFound) { // NSLog(@"TDUtils: key %@ is skipped", key); continue; } // For string-key id value = [dict objectForKey:key]; // If the property type is NSString and the value is array, // join them with "," NSString *propertyType = [properties objectForKey:key]; if ([propertyType isEqualToString:@"NSString"] \ && [value isKindOfClass:[NSArray class]]) { NSArray* arr = (NSArray*) value; NSString* arrString = [arr componentsJoinedByString:@","]; if ([key isEqualToString:@"id"]) { [obj setValue:arrString forKey:@"_id"]; }else { [obj setValue:arrString forKey:key]; } } else { // If the property type is a custom class (not NSDictionary), // and the value is a dictionary, // convert the dictionary to object of that class if ([propertyType isEqualToString:@"NSString"] == NO && [propertyType isEqualToString:@"NSDictionary"] == NO && [value isKindOfClass:[NSDictionary class]]) { // Init a child attribute with respective class Class objCls = NSClassFromString(propertyType); id childObj = [[objCls alloc] init]; // Populate data from the value [Understruck populateObject:childObj fromDictionary:value exclude:nil]; if ([key isEqualToString:@"id"]) { [obj setValue:childObj forKey:@"_id"]; }else{ [obj setValue:childObj forKey:key]; } } // value is a NSNull class else if ([value isKindOfClass:[NSNull class]]) { if ([key isEqualToString:@"id"]) { [obj setValue:@"" forKey:@"_id"]; }else{ [obj setValue:@"" forKey:key]; } } // value is a NSString class and value isEqual @“<null>” else if ([value isKindOfClass:[NSString class]]){ if ([value isEqualToString:@"<null>"]) { if ([key isEqualToString:@"id"]) { [obj setValue:@"" forKey:@"_id"]; }else{ [obj setValue:@"" forKey:key]; } }else{ if ([key isEqualToString:@"id"]) { [obj setValue:value forKey:@"_id"]; }else{ [obj setValue:value forKey:key]; } } } // Else, set value for key else { if ([key isEqualToString:@"id"]) { [obj setValue:value forKey:@"_id"]; }else{ [obj setValue:value forKey:key]; } } } } return obj; } +(id)populateObject:(id)obj fromDictionary:(NSDictionary *)dict { obj = [Understruck populateObject:obj fromDictionary:dict exclude:nil]; return obj; } + (id)objectWithClass:(Class)cls fromDictionary:(NSDictionary *)dict { id obj = [[cls alloc] init]; [Understruck populateObject:obj fromDictionary:dict]; return obj; } + (NSMutableDictionary *)mutableDictionaryForObject:(id)obj { NSDictionary* propertyDict = [Understruck propertiesForClass:[obj class]]; NSMutableDictionary* objDict = [NSMutableDictionary dictionaryWithDictionary:propertyDict]; for (NSString* key in propertyDict) { id val = [obj valueForKey:key]; [objDict setValue:val forKey:key]; } return objDict; } +(NSMutableDictionary *)mutableDictionaryForObject:(id)obj include:(NSArray *)includeArray { NSDictionary* dict = [Understruck dictionaryForObject:obj include:includeArray]; return [NSMutableDictionary dictionaryWithDictionary:dict]; } +(NSDictionary*)dictionaryForObject:(id)obj { NSMutableDictionary *mutableDict = [Understruck mutableDictionaryForObject:obj]; return [NSDictionary dictionaryWithDictionary:mutableDict]; } + (NSDictionary*)dictionaryForObject:(id)obj include:(NSArray*)includeArray { NSDictionary* propertyDict = [Understruck propertiesForClass:[obj class]]; NSMutableDictionary* objDict = [NSMutableDictionary dictionaryWithCapacity:includeArray.count]; for (NSString* key in propertyDict) { if (includeArray && [includeArray indexOfObject:key] == NSNotFound) { DLog(@"TDUtils: key %@ is skipped", key); continue; } id val = [obj valueForKey:key]; [objDict setValue:val forKey:key]; } return objDict; } +(NSArray *)arrayOfClass:(Class)cls fromArray:(NSArray *)array { NSMutableArray *mutableArray = [Understruck mutableArrayOfClass:cls fromArray:array]; NSArray *arrWithClass = [NSArray arrayWithArray:mutableArray]; return arrWithClass; } +(NSMutableArray *)mutableArrayOfClass:(Class)cls fromArray:(NSArray *)array { NSMutableArray *mutableArray = [[NSMutableArray alloc] initWithCapacity:[array count]]; for (id item in array) { // The item must be a dictionary. Otherwise, skip it if ([item isKindOfClass:[NSDictionary class]] == NO) { DLog(@"TDUtils: item inside array must be NSDictionary object"); continue; } // Convert item dictionary to object with predefined class id obj = [Understruck objectWithClass:cls fromDictionary:item]; [mutableArray addObject:obj]; } return mutableArray; } +(id)decodeObject:(Class)clazz from:(id)object{ if (object && (clazz!=NULL)) { if(clazz == [NSDictionary class]||clazz == [NSArray class]){ return object; }else if ([object isKindOfClass:[NSArray class]]) { NSArray *dictArray = (NSArray*)object; NSMutableArray *array = [NSMutableArray new]; for (id obj in dictArray) { id clazzObject = [self decodeObject:clazz from:obj]; if (clazzObject) { [array addObject:clazzObject]; } } return array; }else if ([object isKindOfClass:[NSDictionary class]]){ return [Understruck objectWithClass:clazz fromDictionary:object]; } } return nil; } + (NSString *)populateJsonFrom:(Class)clazz fromArray:(NSArray *)array{ Class obj; NSMutableString *json = [[NSMutableString alloc]init]; for (obj in array) { if (obj) { NSDictionary *dic = [self dictionaryForObject:obj]; [json appendFormat:@"%@",dic]; } } return json; } @end