I am trying to print out an objectified JSON structure that I am saving in multiple dictionaries in a hierarchy. I would like to do this through NSObject's description
method so that each nested dictionary's description method gets called returning its value as well.
Desired Result
//First Hierarchal Level
//Second Hierarchal Level
//Third Hierarchal Level
People =
(
{
Name = Person 1
Gender = Male
Addresses =
(
{
Address Name = Home Address
Street = 123 Street St.
City = NewCity
State = AZ
Zip = 12345
Country = USA
}
)
Cars =
(
{
Make = Ford
Model = NewFord
Insured Drivers =
(
{
Name = Person 1
},
{
Name = Person 2
}
)
}
)
}
)
//NOTE: Sample untested nested structure
However, I have been running into an issue that the return string for each nested dictionary is getting escaped once for each level of the hierarchy that the return string gets passed through.
Actual Result
People = \n (\n {\n Name = Person 1\\\n Gender = Male\\\n Addresses =\\\n ( \\\n {\\\n Address Name = Home Address\\\n Street = 123 Street St.\\\n City = NewCity\\\n State = AZ\\\n Zip = 12345\\\n Country = USA\\\n }\\\n )\\\n Cars = \\\n ( \\\n {\\\\\\\n Make = Ford\\\\\\\n Model = NewFord\\\\\\\n Insured Drivers = \\\\\\\n (\\\\\\\n {\\\\\\\\\\\\\n Name = Person 1\\\\\\\\\\\\\n },\\\\\\\\\\\\\n {\\\\\\\\\\\\\n Name = Person 2\\\\\\\\\\\\\n }\\\\\\\n )\\\\\\\n }\\\n ) \n }\n )
I have read that this has to do with how description adds these escape characters due to it using something like a syslog utility, however I believe the functionality that I desire is available due to how NSArray describes its contents in a similar way to the way in which I would like to. I have tried iterating through the result string and parsing out the escape characters but so far the best result that I have come up with is a non-hierarchical list of all the properties in all dictionaries.
Best Attempt
People =
(
{
Name = Person 1
Gender = Male
Addresses =
(
{
Address Name = Home Address
Street = 123 Street St.
City = NewCity
State = AZ
Zip = 12345
Country = USA
}
)
Cars =
(
{
Make = Ford
Model = NewFord
Insured Drivers =
(
{
Name = Person 1
},
{
Name = Person 2
}
)
}
)
}
)
I was wondering if anyone else has come across this problem and how they overcame it.
Any and all suggestions welcome. Thanks for looking.
UPDATE 1: As per the advice in the comments, I have tried parsing my dictionary objects to JSON strings for printing using the following NSDictionary category method:
-(NSString*)JSONDescription
{
NSError *error;
NSData* jsonData = [NSJSONSerialization dataWithJSONObject:self options:NSJSONWritingPrettyPrinted error:&error];
NSString* json = nil;
if (! jsonData) {
NSLog(@"WARNING: NSDictionary JSONDescription encountered error \"%@\"", error);
} else {
json = [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding];
}
return json;
}
Then, at each level of of my hierarchy, I am calling my dictionary object's JSONDescription
in each description
method. However, it does not appear that the nested object's description
method is being called. This is causing the following exception:
*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'Invalid type in JSON write (Address)'
Example Implementation
#import "Person.h"
#define NAME @"Name"
#define GENDER @"Gender"
#define ADDRESSES @"Addresses"
#define CARS @"Cars"
@implementation Person
-(NSDictionary*)toDictionary{
return @{ NAME: self.name,
GENDER: self.gender,
ADDRESSES: self.addresses,
CARS: self.cars};
}
-(NSString*)description{
return self.toDictionary.JSONDescription;
}
@end
#import "Address.h"
#define ADDRESS_NAME @"Address Name"
#define STREET @"Street"
#define CITY @"City"
#define STATE @"State"
#define ZIP @"Zip"
#define COUNTRY @"Country"
@implementation Address
-(NSDictionary*)toDictionary{
return @{ ADDRESS_NAME: self.addressName,
STREET: self.street,
CITY: self.city,
STATE: self.state,
ZIP: self.zip,
COUNTRY: self.country};
}
-(NSString*)description{
return self.toDictionary.JSONDescription;
}
@end
#import "Car.h"
#define MAKE @"Make"
#define MODEL @"Model"
#define INSURED_DRIVERS @"Insured Drivers"
@implementation Car
-(NSDictionary*)toDictionary{
return @{ MAKE: self.make,
MODEL: self.model,
INSURED_DRIVERS: self.drivers};
}
-(NSString*)description{
return self.toDictionary.JSONDescription;
}
@end
#import "Driver.h"
#define NAME @"Name"
@implementation Car
-(NSDictionary*)toDictionary{
return @{ NAME: self.name};
}
-(NSString*)description{
return self.toDictionary.JSONDescription;
}
@end
The following method might not be the most elegant, but it seems to produce the desired output:
@interface NSObject (MyPrettyPrint)
- (NSString *)prettyPrint;
@end
// === ADDED CODE FOR CUSTOM OBJECTS (1) ===
@protocol ObjectToDictionary <NSObject>
-(NSDictionary *)toDictionary;
@end
// === END OF ADDED CODE (1) ===
@implementation NSObject (MyPrettyPrint)
- (NSString *)prettyPrint
{
BOOL isColl;
return [self prettyPrintAtLevel:0 isCollection:&isColl];
}
- (NSString *)prettyPrintAtLevel:(int)level isCollection:(BOOL *)isCollection;
{
// === ADDED CODE FOR CUSTOM OBJECTS (2) ===
if ([self respondsToSelector:@selector(toDictionary)]) {
NSDictionary *dict = [(id <ObjectToDictionary>)self toDictionary];
return [dict prettyPrintAtLevel:level isCollection:isCollection];
}
// === END OF ADDED CODE (2) ===
NSString *padding = [@"" stringByPaddingToLength:level withString:@" " startingAtIndex:0];
NSMutableString *desc = [NSMutableString string];
if ([self isKindOfClass:[NSArray class]]) {
NSArray *array = (NSArray *)self;
NSUInteger cnt = [array count];
[desc appendFormat:@"%@(\n", padding];
for (id elem in array) {
BOOL isColl;
NSString *s = [elem prettyPrintAtLevel:(level+3) isCollection:&isColl];
if (isColl) {
[desc appendFormat:@"%@", s];
} else {
[desc appendFormat:@"%@ %@", padding, s];
}
if (--cnt > 0)
[desc appendString:@","];
[desc appendString:@"\n"];
}
[desc appendFormat:@"%@)", padding ];
*isCollection = YES;
} else if ([self isKindOfClass:[NSDictionary class]]) {
NSDictionary *dict = (NSDictionary *)self;
[desc appendFormat:@"%@{\n", padding];
for (id key in dict) {
BOOL isColl;
id value = dict[key];
NSString *s = [value prettyPrintAtLevel:(level+3) isCollection:&isColl];
if (isColl) {
[desc appendFormat:@" %@%@ =\n%@\n", padding, key, s];
} else {
[desc appendFormat:@" %@%@ = %@\n", padding, key, s];
}
}
[desc appendFormat:@"%@}", padding ];
*isCollection = YES;
} else {
[desc appendFormat:@"%@", self];
*isCollection = NO;
}
return desc;
}
Example:
NSDictionary *dict = @{@"People": @[
@{
@"Name": @"Person 1",
@"Gender": @"Male",
@"Addresses": @[
@{
@"Address Name": @"Home Address",
@"Street": @"123 Street St.",
@"Zip": @12345
},
],
@"Cars": @[
@{
@"Make": @"Ford",
@"Model": @"NewFord",
@"Insured Drivers": @[
@{@"Name": @"Person 1"},
@{@"Name": @"Person 2"},
]
},
],
},
]};
NSLog(@"People =\n%@", [dict[@"People"] prettyPrint]);
Output:
People = ( { Name = Person 1 Gender = Male Cars = ( { Model = NewFord Make = Ford Insured Drivers = ( { Name = Person 1 }, { Name = Person 2 } ) } ) Addresses = ( { Zip = 12345 Address Name = Home Address Street = 123 Street St. } ) } )
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With