Question

Hello I a class of type NSObject:

ProductDetails *details = [[ProductDetails alloc] init];
details.name = @"Soap1";
details.color = @"Red";
details.quantity = 4;

I want to pass the "details" object to a dictionary.

I did,

NSDictionary *dict = [NSDictionary dictionaryWithObject:details forKey:@"details"];

I am passing this dict to another method which performs a check on JSONSerialization:

if(![NSJSONSerialization isValidJSONObject:dict])

And I am getting a crash on this check. Am I doing anything wrong here? I know that the details I am getting is a JSON object and I am assigning it to the properties in my ProductDetails class.

Please help me. I am a noob in Objective-C.

I now tried:

NSError* error;
NSDictionary* json = [NSJSONSerialization JSONObjectWithData:(NSData*)details options:kNilOptions error:&error];

All I need here is an easy way to convert details to NSData.

I noticed that I have an array inside my object may be thats why all the ways I tried is throwing an exception. However since this question is becoming to big, I have started an another question thread for it where I have displayed the data I am getting inside the object - https://stackoverflow.com/questions/19081104/convert-nsobject-to-nsdictionary

Was it helpful?

Solution 2

NSDictionary *details = {@"name":product.name,@"color":product.color,@"quantity":@(product.quantity)};

NSError *error; 
NSData *jsonData = [NSJSONSerialization dataWithJSONObject:details 
                                                   options:NSJSONWritingPrettyPrinted // Pass 0 if you don't care about the readability of the generated string
                                                     error:&error];

if (! jsonData) {
    NSLog(@"Got an error: %@", error);
} else {
    NSString *jsonString = [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding];
}

Second part's source: Generate JSON string from NSDictionary in iOS

OTHER TIPS

This may well be the easiest way to achieve it. Do import #import <objc/runtime.h> in your class file.

#import <objc/runtime.h>

ProductDetails *details = [[ProductDetails alloc] init];
details.name = @"Soap1";
details.color = @"Red";
details.quantity = 4;
NSDictionary *dict = [self dictionaryWithPropertiesOfObject: details];
NSLog(@"%@", dict);

//Add this utility method in your class.
- (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];
}

As mmackh said, you want to define a custom method for your ProductDetails object that will return a simple NSDictionary of values, e.g.:

@implementation ProductDetails

- (id)jsonObject
{
    return @{@"name"     : self.name,
             @"color"    : self.color,
             @"quantity" : @(self.quantity)};
}

...

Let's assume that we added manufacturer property to our ProductDetails, which referenced a ManufacturerDetails class. We'd just write a jsonObject for that class, too:

@implementation ManufacturerDetails

- (id)jsonObject
{
    return @{@"name"     : self.name,
             @"address1" : self.address1,
             @"address2" : self.address2,
             @"city"     : self.city,
             ...
             @"phone"    : self.phone};
}

...

And then change the jsonObject for ProductDetails to employ that, e.g.:

@implementation ProductDetails

- (id)jsonObject
{
    return @{@"name"         : self.name,
             @"color"        : self.color,
             @"quantity"     : @(self.quantity),
             @"manufacturer" : [self.manufacturer jsonObject]};
}

...

If you have potentially nested collection objects (arrays and/or dictionaries) with custom objects that you want to encode, you could write a jsonObject method for each of those, too:

@interface NSDictionary (JsonObject)

- (id)jsonObject;

@end

@implementation NSDictionary (JsonObject)

- (id)jsonObject
{
    NSMutableDictionary *dictionary = [NSMutableDictionary dictionary];

    [self enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) {
        if ([obj respondsToSelector:@selector(jsonObject)])
            [dictionary setObject:[obj jsonObject] forKey:key];
        else
            [dictionary setObject:obj forKey:key];
    }];

    return [NSDictionary dictionaryWithDictionary:dictionary];
}

@end

@interface NSArray (JsonObject)

- (id)jsonObject;

@end

@implementation NSArray (JsonObject)

- (id)jsonObject
{
    NSMutableArray *array = [NSMutableArray array];

    [self enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
        if ([obj respondsToSelector:@selector(jsonObject)])
            [array addObject:[obj jsonObject]];
        else
            [array addObject:obj];
    }];

    return [NSArray arrayWithArray:array];
}

@end

If you do something like that, you can now convert arrays or dictionaries of your custom objects object into something that can be used for generating JSON:

NSArray *products = @[[[Product alloc] initWithName:@"Prius"  color:@"Green" quantity:3],
                      [[Product alloc] initWithName:@"Accord" color:@"Black" quantity:1],
                      [[Product alloc] initWithName:@"Civic"  color:@"Blue"  quantity:2]];

id productsJsonObject = [products jsonObject];

NSError *error = nil;
NSData *data = [NSJSONSerialization dataWithJSONObject:productsJsonObject options:0 error:&error];

If you're simply trying to save these objects in a file, I'd suggest NSKeyedArchiver and NSKeyedUnarchiver. But if you need to generate JSON objects for your own private classes, you can do something like the above might work.

In .h File

#import <Foundation/Foundation.h>

@interface ContactDetail : NSObject
@property (nonatomic) NSString *firstName;
@property (nonatomic) NSString *lastName;
@property (nonatomic) NSString *fullName;
@property (nonatomic) NSMutableArray *mobileNumbers;
@property (nonatomic) NSMutableArray *Emails;
@property (assign) bool Isopen;
@property (assign) bool IsChecked;
-(NSDictionary *)dictionary;
@end

in .m file

#import "ContactDetail.h"
#import <objc/runtime.h>

@implementation ContactDetail

@synthesize firstName;
@synthesize lastName;
@synthesize fullName;
@synthesize mobileNumbers;
@synthesize Emails;

@synthesize IsChecked,Isopen;

//-(NSDictionary *)dictionary {
//    return [NSDictionary dictionaryWithObjectsAndKeys:self.fullName,@"fullname",self.mobileNumbers,@"mobileNumbers",self.Emails,@"emails", nil];
//}

- (NSDictionary *)dictionary {
    unsigned int count = 0;
    NSMutableDictionary *dictionary = [NSMutableDictionary new];
    objc_property_t *properties = class_copyPropertyList([self class], &count);

    for (int i = 0; i < count; i++) {

        NSString *key = [NSString stringWithUTF8String:property_getName(properties[i])];
        id value = [self valueForKey:key];

        if (value == nil) {
            // nothing todo
        }
        else if ([value isKindOfClass:[NSNumber class]]
                 || [value isKindOfClass:[NSString class]]
                 || [value isKindOfClass:[NSDictionary class]] || [value isKindOfClass:[NSMutableArray class]]) {
            // TODO: extend to other types
            [dictionary setObject:value forKey:key];
        }
        else if ([value isKindOfClass:[NSObject class]]) {
            [dictionary setObject:[value dictionary] forKey:key];
        }
        else {
            NSLog(@"Invalid type for %@ (%@)", NSStringFromClass([self class]), key);
        }
    }
    free(properties);
    return dictionary;
}
@end

if any crash ,You check the property (NSMutableArray,NSString,etc ) in else if condition inside of for.

In Your Controller, in any func...

-(void)addItemViewController:(ConatctViewController *)controller didFinishEnteringItem:(NSMutableArray *)SelectedContact
{
    NSLog(@"%@",SelectedContact);

    NSMutableArray *myData = [[NSMutableArray alloc] init];
    for (ContactDetail *cont in SelectedContact) {
        [myData addObject:[cont dictionary]];
    }

    NSError *error = nil;
    NSData *jsonData = [NSJSONSerialization dataWithJSONObject:myData options:NSJSONWritingPrettyPrinted error:&error];
    if ([jsonData length] > 0 &&
        error == nil){
//        NSLog(@"Successfully serialized the dictionary into data = %@", jsonData);
        NSString *jsonString = [[NSString alloc] initWithData:jsonData
                                                     encoding:NSUTF8StringEncoding];
        NSLog(@"JSON String = %@", jsonString);
    }
    else if ([jsonData length] == 0 &&
             error == nil){
        NSLog(@"No data was returned after serialization.");
    }
    else if (error != nil){
        NSLog(@"An error happened = %@", error);
    }
}

Try this:

#import <objc/runtime.h>

+ (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] ? [obj valueForKey:key] : @"" forKey:key];
    }

    free(properties);

    return [NSDictionary dictionaryWithDictionary:dict];
}

The perfect way to do this is by using a library for serialization/deserialization many libraries are available but one i like is JagPropertyConverter https://github.com/jagill/JAGPropertyConverter

it can convert your Custom object into NSDictionary and vice versa
even it support to convert dictionary or array or any custom object within your object (i.e Composition)

JAGPropertyConverter *converter = [[JAGPropertyConverter alloc]init];
converter.classesToConvert = [NSSet setWithObjects:[ProductDetails class], nil];


//For Object to Dictionary 
NSDictionary *dictDetail = [converter convertToDictionary:detail];
NSDictionary* json = [NSJSONSerialization JSONObjectWithData:dictDetail options:NSJSONWritingPrettyPrinted error:&error];

Try using

NSDictionary *dict = [details valuesForAttributes:@[@"name", @"color"]];

And compare what the dictionary contains. Then try to convert it to JSON. And look at the JSON spec - what data types can go into a JSON encoded file?

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];

You can convert object (say modelObject) to dictionary at runtime with the help of objc/runtime.h class but that has certain limitations and is not recommended.

Considering MVC, mapping logic should be implemented in Model class.

@interface ModelObject : NSObject
@property (nonatomic) NSString *p1;
@property (nonatomic) NSString *p2;
-(NSDictionary *)dictionary;
@end


#import "ModelObject.h"

@implementation ModelObject
-(NSDictionary *)dictionary
{
    NSMutableDictionary *dict = [[NSMutableDictionary alloc] init];

    [dict setValue:self.p1 forKey:@"p1"];// you can give different key name here if you want 
    [dict setValue:self.p2 forKey:@"p2" ];

    return dict;
}
@end

Uses:

NSDictionary *modelObjDict = [modelObj dictionary];

Swift

Now the swift is very popular and most of the SDK's are written in Objective C, we need to convert NSObject to NSDictionary, With the Help of @thatzprem Answer, I wrote an extension for Swift which will convert our NSObject into NSDictionary, then we can use that NSDictionary to simple Dictionary or JSON Object or other purpose. I hope so this will help out the Swift User.

extension NSObject {
func convertNSObjectToNSDictionary() -> [AnyHashable : Any]? {
        var dict: [AnyHashable : Any] = [:]

        var count: UInt32 = 0
        let properties = class_copyPropertyList(type(of: self), UnsafeMutablePointer<UInt32>(mutating: &count)) //as? objc_property_t

        for i in 0..<Int(count) {
            var key: String? = nil
            if let property = properties?[i] as? objc_property_t {
                key = String(utf8String: property_getName(property))
            }
            //dict[key] = (obj as? NSObject)?.value(forKey: key ?? "")
            dict[key] = (self).value(forKey: key ?? "")
        }

        free(properties)

        return dict
    }
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top