Obj-C easy method to convert from NSObject with properties to NSDictionary?

后端 未结 6 934
感情败类
感情败类 2020-12-07 17:17

I ran across something that I eventually figured out, but think that there\'s probably a much more efficient way to accomplish it.

I had an object (an NSObject which

相关标签:
6条回答
  • 2020-12-07 17:35

    Sure thing! Use the objc-runtime and KVC!

    #import <objc/runtime.h>
    
    @interface NSDictionary(dictionaryWithObject)
    
    +(NSDictionary *) dictionaryWithPropertiesOfObject:(id) obj;
    
    @end
    @implementation NSDictionary(dictionaryWithObject)
    
    +(NSDictionary *) dictionaryWithPropertiesOfObject:(id)obj
    {
        NSMutableDictionary *dict = [NSMutableDictionary dictionary];
    
        unsigned count;
        objc_property_t *properties = class_copyPropertyList([obj class], &count);
    
        for (int i = 0; i < count; i++) {
            NSString *key = [NSString stringWithUTF8String:property_getName(properties[i])];
            [dict setObject:[obj valueForKey:key] forKey:key];
        }
    
        free(properties);
    
        return [NSDictionary dictionaryWithDictionary:dict];
    }
    
    @end
    

    And you would use like this:

    MyObj *obj = [MyObj new];    
    NSDictionary *dict = [NSDictionary dictionaryWithPropertiesOfObject:obj];
    NSLog(@"%@", dict);
    
    0 讨论(0)
  • 2020-12-07 17:36

    If the properties had the same names as the keys used to access the dictionary then you could have just used KVC and had valueForKey: instead of objectForKey.

    For example given this dictionary

    NSDictionary *annotation = [[NSDictionary alloc] initWithObjectsAndKeys:
                                 @"A title", @"title", nil];
    

    and this Object

    @interface MyAnnotation : NSObject
    
    @property (nonatomic, copy) NSString *title;
    
    @end
    

    it wouldn't matter if I had an instance of the dictionary or MyAnnotation I could call

    [annotation valueForKey:@"title"];
    

    Obviously that works the other way as well e.g.

    [annotation setValue:@"A title" forKey:@"title"];
    
    0 讨论(0)
  • 2020-12-07 17:46

    This is an old post and Richard J. Ross III's answer is really helpful, but in case of custom objects (an custom class has another custom object in it). However, sometimes properties are other objects and so forth, making the serialization a bit complicated.

    Details * details = [[Details alloc] init];
    details.tomato = @"Tomato 1";
    details.potato = @"Potato 1";
    details.mangoCount = [NSNumber numberWithInt:12];
    
    Person * person = [[Person alloc]init];
    person.name = @"HS";
    person.age = @"126 Years";
    person.gender = @"?";
    person.details = details;
    

    For converting these type of objects (multiple custom objects) into dictionary, I had to modify Richard J. Ross III's Answer a little bit.

    +(NSDictionary *) dictionaryWithPropertiesOfObject:(id)obj
    {
      NSMutableDictionary *dict = [NSMutableDictionary dictionary];
    
      unsigned count;
      objc_property_t *properties = class_copyPropertyList([obj class], &count);
    
      for (int i = 0; i < count; i++) {
          NSString *key = [NSString stringWithUTF8String:property_getName(properties[i])];
          Class classObject = NSClassFromString([key capitalizedString]);
          if (classObject) {
            id subObj = [self dictionaryWithPropertiesOfObject:[obj valueForKey:key]];
            [dict setObject:subObj forKey:key];
          }
          else
          {
            id value = [obj valueForKey:key];
            if(value) [dict setObject:value forKey:key];
          }
       }
    
       free(properties);
    
       return [NSDictionary dictionaryWithDictionary:dict];
    }
    

    I hope it will help someone. Full credit goes to Richard J. Ross III.

    0 讨论(0)
  • 2020-12-07 17:50

    To complete the method of Richard J. Ross, this one works with NSArray of custom object.

    +(NSDictionary *) dictionaryWithPropertiesOfObject:(id)obj
    {
        NSMutableDictionary *dict = [NSMutableDictionary dictionary];
    
        unsigned count;
        objc_property_t *properties = class_copyPropertyList([obj class], &count);
    
        for (int i = 0; i < count; i++) {
            NSString *key = [NSString stringWithUTF8String:property_getName(properties[i])];
            Class classObject = NSClassFromString([key capitalizedString]);
    
            id object = [obj valueForKey:key];
    
            if (classObject) {
                id subObj = [self dictionaryWithPropertiesOfObject:object];
                [dict setObject:subObj forKey:key];
            }
            else if([object isKindOfClass:[NSArray class]])
            {
                NSMutableArray *subObj = [NSMutableArray array];
                for (id o in object) {
                    [subObj addObject:[self dictionaryWithPropertiesOfObject:o] ];
                }
                [dict setObject:subObj forKey:key];
            }
            else
            {
                if(object) [dict setObject:object forKey:key];
            }
        }
    
        free(properties);
        return [NSDictionary dictionaryWithDictionary:dict];
    }
    
    0 讨论(0)
  • 2020-12-07 17:54

    You also can use the NSObject+APObjectMapping category which is available on GitHub: https://github.com/aperechnev/APObjectMapping

    It's a quit easy. Just describe the mapping rules in your class:

    #import <Foundation/Foundation.h>
    #import "NSObject+APObjectMapping.h"
    
    @interface MyCustomClass : NSObject
    @property (nonatomic, strong) NSNumber * someNumber;
    @property (nonatomic, strong) NSString * someString;
    @end
    
    @implementation MyCustomClass
    + (NSMutableDictionary *)objectMapping {
      NSMutableDictionary * mapping = [super objectMapping];
      if (mapping) {
        NSDictionary * objectMapping = @{ @"someNumber": @"some_number",
                                          @"someString": @"some_string" };
      }
      return mapping
    }
    @end
    

    And then you can easily map your object to dictionary:

    MyCustomClass * myObj = [[MyCustomClass alloc] init];
    myObj.someNumber = @1;
    myObj.someString = @"some string";
    NSDictionary * myDict = [myObj mapToDictionary];
    

    Also you can parse your object from dictionary:

    NSDictionary * myDict = @{ @"some_number": @123,
                               @"some_string": @"some string" };
    MyCustomClass * myObj = [[MyCustomClass alloc] initWithDictionary:myDict];
    
    0 讨论(0)
  • 2020-12-07 17:57

    There are so many solutions and nothing worked for me as I had a complex nested object structure. This solution takes things from Richard and Damien but improvises as Damien's solution is tied to naming keys as class names.

    Here is the header

    @interface NSDictionary (PropertiesOfObject)
    +(NSDictionary *) dictionaryWithPropertiesOfObject:(id)obj;
    @end
    

    Here is the .m file

    @implementation NSDictionary (PropertiesOfObject)
    
    static NSDateFormatter *reverseFormatter;
    
    + (NSDateFormatter *)getReverseDateFormatter {
    if (!reverseFormatter) {
        NSLocale *locale = [[NSLocale alloc] initWithLocaleIdentifier:@"en_US_POSIX"];
        reverseFormatter = [[NSDateFormatter alloc] init];
        [reverseFormatter setDateFormat:@"yyyy-MM-dd'T'HH:mm:ss.SSS'Z'"];
        [reverseFormatter setLocale:locale];
    }
    return reverseFormatter;
    }
    
     + (NSDictionary *)dictionaryWithPropertiesOfObject:(id)obj {
    NSMutableDictionary *dict = [NSMutableDictionary dictionary];
    
    unsigned count;
    objc_property_t *properties = class_copyPropertyList([obj class], &count);
    
    for (int i = 0; i < count; i++) {
        NSString *key = [NSString stringWithUTF8String:property_getName(properties[i])];
        id object = [obj valueForKey:key];
    
        if (object) {
            if ([object isKindOfClass:[NSArray class]]) {
                NSMutableArray *subObj = [NSMutableArray array];
                for (id o in object) {
                    [subObj addObject:[self dictionaryWithPropertiesOfObject:o]];
                }
                dict[key] = subObj;
            }
            else if ([object isKindOfClass:[NSString class]]) {
                dict[key] = object;
            } else if ([object isKindOfClass:[NSDate class]]) {
                dict[key] = [[NSDictionary getReverseDateFormatter] stringFromDate:(NSDate *) object];
            } else if ([object isKindOfClass:[NSNumber class]]) {
                dict[key] = object;
            } else if ([[object class] isSubclassOfClass:[NSObject class]]) {
                dict[key] = [self dictionaryWithPropertiesOfObject:object];
            }
        }
    
    }
    return dict;
    }
    
    @end
    
    0 讨论(0)
提交回复
热议问题