NSJSONSerialization - Core Data relationships to JSON - ios

I've been trying to create a class to allow me to output core data out to JSON.
I have managed to get it working to a point, however I seem to have hit a brick wall on a outputting relationships
NSMutableArray * objectsArray = [[NSMutableArray alloc] init];
for (NSManagedObject * object in array) {
if([NSJSONSerialization isValidJSONObject:object]) {
[objectsArray addObject:object];
} else {
NSMutableDictionary *fields = [NSMutableDictionary dictionary];
for (NSAttributeDescription *attribute in [[object entity] properties]) {
NSString *attributeName = attribute.name;
id attributeValue = [object valueForKey:attributeName];
if([results length] > 0)
{
NSArray *chunks2 = [results componentsSeparatedByString: #","];
for (NSString * string in chunks2) {
if([string.lowercaseString isEqualToString:attributeName.lowercaseString])
{
[fields setObject:[NSString stringWithFormat:#"%#",attributeValue] forKey:attributeName];
break;
}
}
}
else
{
if (attributeValue) {
[fields setObject:[NSString stringWithFormat:#"%#",attributeValue] forKey:attributeName];
}
}
}
[objectsArray addObject:fields];
}
}
NSError *error;
NSData * JSONData = [NSJSONSerialization dataWithJSONObject:objectsArray options:kNilOptions error:&error];
And this outputs data fine aslong as I do not have a relationship for example a one -> many or many -> one
It outputs the following
{
"mySegmentation": "(null)",
"number": "9452062"
},
{
"mySegmentation": "<NSManagedObject: 0x212050b0> (entity: SegmentationCodes; id: 0x212090b0 <x-coredata://BEC52F5F-EA26-4CFF-BCCB-09DA163F465D/SegmentationCodes/p13> ; data: <fault>)",
"number": "9448502"
},
How can I get it to also indent in and output the information from the relationship?
I have been scratching my head for a while on this and would appreciate the help
Thanks Matt

From the documentation:
An object that may be converted to JSON must have the following properties:
The top level object is an NSArray or NSDictionary.
All objects are instances of NSString, NSNumber, NSArray,
NSDictionary, or NSNull.
All dictionary keys are instances of NSString.
Numbers are not NaN or infinity.
So, what you have to do is compose a dictionary or array with dictionaries, arrays, strings, numbers, nulls.
Normally relationships in CoreData are not sorted, so NSSets, you have to generate a NSArray from the set (therefor a nice method from Apple exists) and put it as value in the dictionary for the specific key.
Then run - dataWithJSONObject:options:error: for example (as you did before) and retrieve the correct JSON.
Not sure if the indention is right. You have to check that out.
Thats it, hopefully

Related

Comparing two NSDictionaries and Find Difference

I am working on an iOS app, where I will be getting a JSON Object from server, which will be populated on a UITableView.
User can change values on tableview, Hence resulting in a new JSON.
Now I want to send only delta (Difference of Two JSON Objects) back to server.
I know I can traverse both Objects for finding delta. But just wish to know is there any easy solution for this problem.
Ex:
NSDictionary *dict1 = {#"Name" : "John", #"Deptt" : #"IT"};
NSDictionary *dict2 = {#"Name" : "Mary", #"Deptt" : #"IT"};
Delta = {#"Name" : "Mary"}
Considering new value is Mary for key name;
Thanks In Advance
isEqualToDictionary: Returns a Boolean value that indicates whether the contents of the receiving dictionary are equal to the contents of another given dictionary.
if ([NSDictionary1 isEqualToDictionary:NSDictionary2) {
NSLog(#"The two dictionaries are equal.");
}
Two dictionaries have equal contents if they each hold the same number of entries and, for a given key, the corresponding value objects in each dictionary satisfy the isEqual: test.
Here's how to get all the keys with non-matching values. What to do with those keys is app level question, but the most informative structure would include an array of mismatched values from both dictionaries, as well has handle keys from one that are not present in the other:
NSMutableDictionary *result = [#{} mutableCopy];
// notice that this will neglect keys in dict2 which are not in dict1
for (NSString *key in [dict1 allKeys]) {
id value1 = dict1[key];
id value2 = dict2[key];
if (![value1 equals:value2]) {
// since the values might be mismatched because value2 is nil
value2 = (value2)? value2 : [NSNull null];
result[key] = #[value1, value2];
}
}
// for keys in dict2 that we didn't check because they're not in dict1
NSMutableSet *set1 = [NSMutableSet setWithArray:[dict1 allKeys]];
NSMutableSet *set2 = [NSMutableSet setWithArray:[dict2 allKeys]];
[set2 minusSet:set1]
for (NSString *key in set2) {
result[key] = #[[NSNull null], dict2[key]];
}
There are certainly more economical ways to do it, but this code is optimized for instruction.
Just enumerate through and compare the dictionaries key-by-key. This will output any differences as well as any unmatched keys on either side, you can tweak the logic depending on exactly what you want to include.
- (NSDictionary *)delta:(NSDictionary *)dictionary
{
NSMutableDictionary *result = NSMutableDictionary.dictionary;
// Find objects in self that don't exist or are different in the other dictionary
[self enumerateKeysAndObjectsUsingBlock:^(id _Nonnull key, id _Nonnull obj, BOOL * _Nonnull stop) {
id otherObj = dictionary[key];
if (![obj isEqual:otherObj]) {
result[key] = obj;
}
}];
// Find objects in the other dictionary that don't exist in self
[dictionary enumerateKeysAndObjectsUsingBlock:^(id _Nonnull key, id _Nonnull obj, BOOL * _Nonnull stop) {
id selfObj = self[key];
if (!selfObj) {
result[key] = obj;
}
}];
return result;
}

Finding distinct array elements based on dictionary key

I have two arrays of key-value pairs. Both these arrays contain different key-value pairs. I want to find elements in the first array that are not part of the second array based on a particular key.
Example:
1st Array - [{id=1, name="foo"},
{id=2, name="bar"}]
2nd Array - [{id=2, name="abc"},
{id=1, name="xyz"}]
Is there a way I can implement the same?
Right now I enumerate through the two arrays like so:
for (NSDictionary *eachPlayer in 1stArray) {
for (NSDictionary *eachPrediction in 2ndArray) {
if (eachPrediction[kId] != eachPlayer[kId]) {
[self.predictPlayerArray addObject:eachPlayer];
}
}
}
But this fails in the above case and adds both the values to the predictionPlayerArray - in the first iteration it adds 1 and in the forth iteration it adds 2. How do I prevent that from happening?
Thanks.
EDIT
I seem to have solved it this way. Not the best solution but it seems to be working:
for (NSDictionary *eachPlayer in arrayOne) {
for (NSDictionary *eachPrediction in arrayTwo) {
if (eachPrediction[kId] == eachPlayer[kId]) {
if ([self.predictPlayerArray containsObject:eachPlayer]) {
[self.predictPlayerArray removeObject:eachPlayer];
}
break;
}
else {
[self.predictPlayerArray addObject:eachPlayer];
}
self.predictPlayerArray = [self.predictPlayerArray valueForKeyPath:#"#distinctUnionOfObjects.self"];
}
}
Something like this should do:
NSArray *array1 = #[#{#"1":#"foo"},#{#"2":#"bar"},#{#"3":#"abc"}];
NSArray *array2 = #[#{#"2":#"abc"},#{#"1":#"abc"},#{#"4":#"foo"}];
NSMutableSet *result = [NSMutableSet new];
for (NSDictionary *dict1 in array1){
[dict1 enumerateKeysAndObjectsUsingBlock:^(id key1, id obj1, BOOL *stop1) {
for (NSDictionary *dict2 in array2) {
[dict2 enumerateKeysAndObjectsUsingBlock:^(id key2, id obj2, BOOL *stop2) {
if ([obj2 isEqual:obj1]){
[result addObject:#{key1:obj1}];
*stop2 = YES;
}
}];
}
}];
}
NSLog(#"result %#", result);
As you has nested dictionaries you should iterate also in them and finally store the result in a set that would prevent to have duplicate entries (if you use a NSMutableArray you will have twice {3:abc})
The log output is:
2015-02-03 13:53:07.897 test[19425:407184] result {(
{
1 = foo;
},
{
3 = abc;
}
)}

Remove entries from NSMutableDictionary based on match on NSString value

After a network call to the Instagram API, I get back a responseDictionary NSDictionary delegate with the following Key/Value structure:
{
data = (
{
bio = "Los Angeles/Orange County Realtor\U00ae \n\U6d1b\U6749\U77f6\U623f\U5730\U4ea7\U7ecf\U7eaa\U4eba\nCall/Text/WhatsApp: (310) 717-1321\nEmail: Jxxxcom\nWeChat (\U5fae\U4fe1): xx";
"full_name" = "xx yy (\U7530\U4f73\U6dfc) Rx Realty";
id = 25354408;
"profile_picture" = "http://scontent-a.cdninstagram.com/hphotos-xpa1/outbound-distillery/t0.0-20/OBPTH/profiles/profile_xxx_75sq_1391378894.jpg";
username = jxxi;
website = "http://www.Jxghty.com";
},
The profile_picture key often has an NSString value that contains anonymousUser (for the users who didn't set any profile pictures).
I am looking to remove those entries from my responseDictionary as follows:
//Create mutable copy of IG responseDictionary
NSMutableDictionary *dictCleanAvatars = [responseDictionary mutableCopy];
NSLog(#"Log dictCleanAvatars after mutableCopy IG response: %#", dictCleanAvatars);
NSArray *keys = [dictCleanAvatars allKeys]; //get all the keys
NSUInteger k2 = [dictCleanAvatars count];
NSLog(#"k2 in dictCleanAvatars before cleanup is: %lu", (unsigned long)k2);
for (int i = 0; i<k2; i++)
{
if ([[dictCleanAvatars objectForKey:[keys objectAtIndex:i]] isKindOfClass:[NSString class]])
{
//if its an NSString - don't want an exception if its another type of object
NSLog(#"Yes, objectAtIndex:i us Kind ofClass NSString for i = %d", i);
if ([[dictCleanAvatars objectForKey:[keys objectAtIndex:i]] rangeOfString:#"anonymousUser"].location != NSNotFound)
{
NSLog(#"Yes, anonymousUser identified in objectAtIndex:i for i = %d", i);
//if object has the key word im looking for
[dictCleanAvatars removeObjectForKey:[keys objectAtIndex:i]]; //remove the key
NSLog(#"That's dictCleanAvatars after loop %d: %#", i, dictCleanAvatars);
}
}
}
But this doesn't work.
Would value feedback from more experience iOS developers.
If you're trying to build an array that includes everything from the data key's array, but omitting those dictionaries for which profile_picture contains the string "AnonymousUser", you can use NSPredicate:
NSArray *dataArray = responseDictionary[#"data"];
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"not (profile_picture contains 'AnonymousUser')"];
NSArray *filteredArray = [dataArray filteredArrayUsingPredicate:predicate];
Or you can use predicateWithBlock:
NSPredicate *predicate = [NSPredicate predicateWithBlock:^BOOL(NSDictionary *evaluatedObject, NSDictionary *bindings) {
return [evaluatedObject[#"profile_picture"] rangeOfString:#"AnonymousUser"].location == NSNotFound;
}];
BTW, if you already have a mutable array, you can also remove entries from it using filterUsingPredicate, using the above predicates:
NSMutableArray *mutableDataArray = [responseDictionary[#"data"] mutableCopy];
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"not (profile_picture contains 'AnonymousUser')"];
[mutableDataArray filterUsingPredicate:predicate];
If, on the other hand, you don't want to remove entire dictionaries from the array of dictionaries, but rather want to simply remove the occurrences of profile_picture for which "AnonymousUser" is present, you want to ensure that not only is the array mutable, but so are its constituent dictionaries.
The easiest way of doing this is to specify the NSJSONReadingMutableContainers option when parsing the JSON. Then you can just iterate through the NSMutableDictionary entries, removing the profile_picture entries with a profile_picture with "AnonymousUser" in them:
NSMutableDictionary *responseDictionary = [NSJSONSerialization JSONObjectWithData:responseData options:NSJSONReadingMutableContainers error:&error];
NSMutableArray *mutableDataArray = responseDictionary[#"data"];
for (NSMutableDictionary *dictionary in mutableDataArray) {
NSString *profilePicture = dictionary[#"profile_picture"];
if ([profilePicture rangeOfString:#"AnonymousUser"].location != NSNotFound) {
[dictionary removeObjectForKey:#"profile_picture"];
}
}
If, however, you can't specify the NSJSONReadingMutableContainers option when you parse the JSON and are stuck with a immutable collection, you need to make a mutable copy of it. Unfortunately, a simple mutableCopy of the array won't make the member dictionaries mutable themselves, but you can use a Core Foundation call to CFPropertyListCreateDeepCopy to make a mutable array with mutable entries, which you can then modify:
NSMutableArray *mutableDataArray = CFBridgingRelease(CFPropertyListCreateDeepCopy(kCFAllocatorDefault, (CFArrayRef)responseDictionary[#"data"], kCFPropertyListMutableContainers));
Then you can use the above for loop, iterating through this array's dictionary entries, removing the offending profile_picture entries.
if [[dictCleanAvatars objectForKey:[keys objectAtIndex:i]] isEqualToString#"anonymousUser"] {
The problem is, suppose [dictCleanAvatars objectForKey:[keys objectAtIndex:i]] is not an NSString? You might want to check for that first.
If the only field you are looking at is profile_picture, I would go with a less generic approach which is much more readable and understandable
This code works for me
- (void)testExample
{
NSDictionary *dictionary = #{ #"data": #[ #{ #"bio": #"blah blah", #"profile_picture": #"some stuff anonymousUser other stuff" },
#{ #"bio": #"some other object", #"profile_picture": #"some other profile picture link" }] };
// dictionary is a mock of the data you provided
NSArray *data = [dictionary objectForKey:#"data"];
for (NSDictionary * avatarDict in data) {
NSMutableDictionary *mdict = [avatarDict mutableCopy];
id ppid = [mdict objectForKey:#"profile_picture"];
if ([ppid isKindOfClass:[NSString class]]) {
NSString *pp = (NSString *)ppid;
if ([pp rangeOfString:#"anonymousUser"].location != NSNotFound) {
[mdict removeObjectForKey:#"profile_picture"];
}
}
NSLog(#"altered dictionary: %#", mdict);
}
}
Output:
2014-08-13 10:53:36.727 test[11981:60b] altered dictionary: {
bio = "blah blah";
}
2014-08-13 10:53:36.728 test[11981:60b] altered dictionary: {
bio = "some other object";
"profile_picture" = "some other profile picture link";
}

Parsing Nested NSDictionary and NSArray Data in xCode

I have a complex JSON file that I need to parse into a CoreData table. Currently, I capture the data into an NSArray with this format and the following 6 elements:
2013-08-29 10:54:04.930 iTrackTest[1542:c07] athleteRecords[0]: #SchoolID
2013-08-29 10:54:04.930 iTrackTest[1542:c07] athleteRecords[1]: #LastName
2013-08-29 10:54:04.930 iTrackTest[1542:c07] athleteRecords[2]: #Gender
2013-08-29 10:54:04.931 iTrackTest[1542:c07] athleteRecords[3]: SchType
2013-08-29 10:54:04.931 iTrackTest[1542:c07] athleteRecords[4]: #FirstName
2013-08-29 10:54:04.931 iTrackTest[1542:c07] athleteRecords[5]: #IDAthlete
First question, it appears that SchType is a k-dimensional NSArray of NSDictionaries. Is that true?
I have been capturing simpler, single-tiered JSON files using code from Paul Hegarty of Stanford:
dispatch_async(fetchQ, ^{
NSArray *athleteRecords;
athleteRecords = [AthleticNetDataFetcher retrieveDataForAthleteWithID:athleteID];
NSLog(#"In %#: athleteRecords has %d records",NSStringFromClass([self class]), [athleteRecords count]);
NSLog(#"NSArray with athleteRecords: %#", athleteRecords);
[document.managedObjectContext performBlock:^{
int iCount=0;
for (NSDictionary *athleteInfo in athleteRecords) {
[self resultsWithAthleteInfoForAthleteWithID:athleteInfo inManagedObjectContext:document.managedObjectContext];
NSLog(#"athleteRecords[%d]: %#", iCount, athleteInfo);
iCount++;
}
[document saveToURL:document.fileURL forSaveOperation:UIDocumentSaveForOverwriting completionHandler:NULL];
}];
});
I need data elements from each node for every record in my CoreData table. For example, SchoolName from School node, IDSeason from Season node, and all elements from Results node would be written to a single CoreData table row (record).
Do I need to resort to dot notation and abandon the iteration through the NSArray or do I need to capture multiple NSArrays each with data further down the nodes? Having a hard time getting my head around this.
Thanks!
Not sure why I had such a difficult time getting my head around this, but Hot Licks got me on the right track.
Here is what I learned that might be helpful to others:
If you have multiple NSDictionaries embedded within an array, it is much simpler to parse these sub-dictionaries in other methods.
+(void)parseDivisionBranches:(NSDictionary *)schTypeDictionary usingStudentInfoFrom:(NSDictionary *)myAthleteInfo
intoManagedDoc: (UIManagedDocument *)document
{
NSArray* schoolDivisions = [self wrapDictionaryInArrayIfNecessary:[schTypeDictionary valueForKeyPath:#"School.SchoolDivision"]];
for (NSDictionary* schoolDivision in schoolDivisions) {
[MarksFromMeets parseDictionaryWithXcMarksForAthlete:(NSString*) [myAthleteInfo objectForKey:#"athlete_ID"]
fromDictionary:(NSDictionary *)schoolDivision
intoThisManagedDoc:(UIManagedDocument *)document];
}
}
In instances where only a single NSDictionary is passed at a particular level of the tree, it is simpler to embed that NSDictionary inside an NSArray so that you can use the same code to extract data; therefore, I always check to see if have an NSArray or NSDict.
+ (NSArray*) wrapDictionaryInArrayIfNecessary:(NSObject*)dictionaryMasquaradingAsAnArray
{
NSMutableArray* newArray = [[NSMutableArray alloc] init];
if([dictionaryMasquaradingAsAnArray isKindOfClass:[NSArray class]]) {
newArray = [dictionaryMasquaradingAsAnArray copy];
}else if([dictionaryMasquaradingAsAnArray isKindOfClass:[NSDictionary class]]) {
[newArray addObject:dictionaryMasquaradingAsAnArray];
}else {
NSString *className = NSStringFromClass([dictionaryMasquaradingAsAnArray class]);
NSLog(#"ERROR - dictionaryMasquaradingAsAnArray of %# class", className);
newArray = nil;
}
return newArray;
}
Then parse each sub-dictionary in turn by calling the method associated with the branch of the data tree, in this case:
+ (void)parseDictionaryWithXcMarksForAthlete:(NSString*)withAthleteID
fromDictionary:(NSDictionary *)dictionary
intoThisManagedDoc:(UIManagedDocument *)document
{
NSArray* seasons = [self wrapDictionaryInArrayIfNecessary:[dictionary valueForKeyPath:#"Season"]];
BOOL* parsedSeasonData;
for (NSDictionary* season in seasons) {
parsedSeasonData = [self parseDictionaryWithSeasonsListings:(NSString*)withAthleteID
fromDictionary:(NSDictionary *)season
intoThisManagedDoc:(UIManagedDocument *)document];
}
}
At some nodes, I had to capture data and pass it along down the chain for use later when I would ultimately write a record to CoreData. Again, thanks to Hot Licks and hope this helps others.

NSMutableArray insert object at last index

I am trying to copy the objects content of a NSDictionary to a NSMutableArray, and I am using the following code :
// Use when fetching binary data
NSData *responseData = [request responseData];
// View the data returned - should be ready for parsing.
resultsDictionary = [responseData objectFromJSONData];
NSLog(#"ResultsDictionary:%#", resultsDictionary);
self.OnlineObjects = [[[NSMutableArray alloc] init] autorelease];
for (NSDictionary * dataDict in resultsDictionary) {
[OnlineObjects insertObject:dataDict atIndex:0];
}
NSLog(#"OnlineObjects:%#", OnlineObjects);
This is working as i am getting all objects from the Dictionary, but the objects order have been revers, first object is now last ...
How can tell the insertObject to add the object at the last index ?
Thanks
You can use the addObject: method instead.
To get rid of the hash order problem get allKeys, sort the array and then use the elements as keys to get the objects in proper order.
Verbose example (for integer keys):
NSArray *indices = [[resultsDictionary allKeys] sortedArrayUsingComparator:^(id obj1, id obj2) {
if ( [obj1 intValue] > [obj2 intValue] ) {
return (NSComparisonResult)NSOrderedDescending;
}
if ( [obj1 intValue] < [obj2 intValue] ) {
return (NSComparisonResult)NSOrderedAscending;
}
return (NSComparisonResult)NSOrderedSame;
}];
for (int i = 0; i < [indices count]; i++) {
NSDictionary *obj = [resultsDictionary objectForKey:[indices objectAtIndex:i]];
[OnlineObjects addObject:obj];
}
The order of the elements in a NSDictionary is undefined, you don't know in which order they will be retrieved from the dictionary. The only way to do have the array sorted is to sort it once all the values from the dictionary are transferred to the array.
Two things you should know:
NSDictionary is a key-value container, which does not guarantee the order of the objects. You have no way to ensure that the order of inserting will be mantained when reading by using this data structure. Check other strategies if order is important for you, but do not rely on NSDictionary for this.
You have a couple of methods to extract the info of the keys and data: allKeys and allValues. Use them instead of creating your own.

Resources