Convert 2D NSMutableArray in JSON format - JSONKit - iphone

I have the following array
NSMutableArray* answers;
Each element of answers is itself an array of objects.
I need to convert the above 2D array into appropriate JSON format (using the JSONKit framework), so that it can be passed to a php application and decoded thereafter...
The individual objects have the following structure:
#interface Answer : NSObject {
//NSString* answerId;
NSString* answer;
NSString* questionId;
}
//#property (nonatomic, retain) NSString* answerId;
#property (nonatomic, retain) NSString* answer;
#property (nonatomic, retain) NSString* questionId;
#end
That is that each element of answers is an array of Answer Objects. essentially what I need is to encode the relevant data in each answer object into JSON format using the JSONKit framework so that it can be posted to a php app and decoded....
Essentially I need somthing of the form:
{{"answer":"1","questionId":"1"}, {{"answer":"5","questionId":"2"},......}

The JSONKit, like all other JSON frameworks, does not play well with custom objects. To that end, you need to iterate through your object and put them into objects that the JSONKit can understand (NSArrays and NSDictionaries). Something like this:
NSMutableArray *jAnswers = [[[NSMutableArray alloc] init] autorelease];
for(Answer *answ in answers)
{
NSMutableDictionary *jAnswer = [[[NSMutableDictionary alloc] init] autorelease];
[jAnswer addObject: answ.answer forKey: #"answer"];
[jAnswer addObject: answ.questionId forKey: #"questionId"];
[jAnswers addObject: jAnswer];
}
NSString *jAnswersJSONFormat = [jAnswers JSONString];
would give you:
[{"answer": "1", "questionId": "1"}, {"answer": "5", "questionId": "2"}, ...]

JSONKit does seem to offer both a delegate-based and a block-based method for serializing unsupported object types. My guess, not having used the framework, is that you call one of those versions of the serialization methods and pass a delegate/selector pair or a block which returns a JSON-serializable object in place of an unsupported object type.
You'll want one of these category methods on NSArray:
- (NSString *)JSONStringWithOptions:(JKSerializeOptionFlags)serializeOptions serializeUnsupportedClassesUsingDelegate:(id)delegate selector:(SEL)selector error:(NSError **)error;
- (NSString *)JSONStringWithOptions:(JKSerializeOptionFlags)serializeOptions serializeUnsupportedClassesUsingBlock:(id(^)(id object))block error:(NSError **)error;

Related

JSON Dictionary to Core Data

I have a JSON response that I turn into a Dictionary like so:
NSError *error;
self.restKitResponseDict = [NSDictionary dictionaryWithDictionary:[NSJSONSerialization JSONObjectWithData:response.body options:0 error:&error]];
I have a core data class that has the following attributes/properties:
name
image_url
When I log the restKitResponseDict from above I see that image_url is listed as "image_url" like this:
name = Rock;
"image_url" = "http://f.cl.ly/items/122s3f1M1E1p432B211Q/catstronaut.jpg";
Is this why KVC is crashing on
[CoreDataClass setValuesForKeysWithDictionary:self.restKitResponseDict];
like this:
'[<CoreDataClass 0x14132c> setValue:forUndefinedKey:]: this class is not key value coding-compliant for the key image_url.'
Do the quotes matter? Should I ask my server guy to get rid of the underscore that's likely causing it?
Core Data Class:
#import <Foundation/Foundation.h>
#import <CoreData/CoreData.h>
#interface CoreDataClass : NSManagedObject
#property (nonatomic, retain) NSString * name;
#property (nonatomic, retain) NSString * image_url;
#end
#implementation CoreDataClass
#dynamic name;
#dynamic image_url;
#end
You are sending the method to the class object:
[CoreDataClass setValuesForKeysWithDictionary:self.restKitResponseDict];
when you probably want to send it to the actual CoreDataClass instance:
[coreDataClassObject setValuesForKeysWithDictionary:self.restKitResponseDict];
EDIT
what's the simplest way to init the object from the class? – Eric
It's a subclass of NSManagedObject, so you use the normal Core Data methods. One way to create a new object:
CoreDataClass *coreDataObject = [NSEntityDescription
insertNewObjectForEntityForName:#"YOUR_ENTITY_NAME"
inManagedObjectContext:managedObjectContext];
If you need basic information about using Core Data, see the Core Data Programming Guide: http://developer.apple.com/library/ios/#documentation/cocoa/conceptual/coredata/cdProgrammingGuide.html#//apple_ref/doc/uid/TP30001200-SW1
The NSLog() with the %# format uses the description method to print objects, and the description of a NSDictionary puts quotation marks around all keys and values that contain any special characters, such as the underscore. For example
NSDictionary *dict = #{
#"key1" : #"value_1",
#"key_2" : #"value2"
};
NSLog(#"dict=%#", dict);
produces
2012-08-25 18:15:33.553 test27[3416:c07] dict={
key1 = "value_1";
"key_2" = value2;
}
Therefore the key in your JSON dictionary does not really have quotation marks, and the underscore is probably not the cause of the error.
The error message indicates that the managed object does not have an image_url attribute, therefore you should check that.

RestKit: mapping of dynamic attribute (not a NSDictionary as in the example)

I'm using RestKit in my iOS project, and figured non-trivial problem and can't find solution for it.
I have a json:
[{
"name": "restkit",
"downloads": 2
},
{
"name": "restkit",
"rating": 10.0
}]
and data model: Model.h
#interface Model : NSObject
#property (nonatomic, strong) NSString * name;
#property (nonatomic, strong) NSString * key;
#property (nonatomic, strong) NSString * value;
#end
Those JSON objects loaded in one array, and one of the attributes is a "dynamic" attribute.
After mapping performed in a RestKit I want to be able to have 2 records:
name: "restkit", key: "downloads", value: 2
name: "restkit", key: "rating", value: 10.0
Question: how to map JSON in the beginning into 2 NSObjects as shown in the example above?
This is how I initialize mapping using RestKit and firing requests:
// during app initialization I setup mappings:
RKObjectMapping *modelMapping = [RKObjectMapping mappingForClass:[Model class]];
[metricDataMapping mapKeyPath:#"name" toAttribute:#"name"];
...... -- something should go here to support that dynamic stuff
[[RKObjectManager sharedManager].mappingProvider addObjectMapping:metricDataMapping];
// in the view controller when loading data
[[RKObjectManager sharedManager] loadObjectsAtResourcePath:url usingBlock:^(RKObjectLoader *loader){
loader.objectMapping = [[RKObjectManager sharedManager].mappingProvider objectMappingForClass: [MetricData class]];
loader.onDidLoadObjects = ^(NSArray * objects){
self.dataArray = objects;
};
}];
The problem, is that keys "downloads" and "rating" are dynamic, and it could be any word. I need to parse that 'on-the-fly' and show in UI.
Restkit does support such behavior, but only for nested dictionaries.
You can access the dynamically named properties using key-value coding. After you had made an NSDictionary out of the JSON, do this:
[self setValue:[dictionary objectForKey:#"downloads"] forKey:#"downloads"];
etc.
I did use key-value validation on the model triggered by RestKit to set correct metric key and value to the generic key I want.

Access NSDictionary via dot notation?

Is there a way via dot notation to access the values of keys in an NSDictionary like this?
NSDictionary *returnVal = [NSDictionary dictionaryWithObjectsAndKeys:#"Saturn", #"name", #"Gas Giant", #"type", nil];
NSLog(#"VALUE: %#", [returnVal valueForKey:#"name"]); // This is how I am doing it now.
There is no dot syntax for NSDictionary, but should consider using objectForKey: instead of valueForKey:
Difference between objectForKey and valueForKey?
Not really, no.
The dot notation is a shorthand way of calling a method with that selector name. In other words, this...
NSLog(#"Hello, %#", foo.bar.name);
...is the same as this...
NSLog(#"Hello, %#", [[foo bar] name]);
When I say "same", I mean they are compiled down to the same code. It's just syntactic sugar.
A plain NSDictionary won't act that way. You could sort of fake it with Key Value Coding, which lets you call valueForKeyPath to get properties like this:
NSLog(#"Hello, %#", [foo valueForKeyPath:#"bar.name"]);
If you really wanted to be able to write foo.bar.name in your code, however, you'd have to make a custom class that overrides forwardInvocation:; this lets you catch an unknown message to an object and do something else with it besides throw an error. In this case, you could change the unknown selector to a lookup on an NSDictionary instance it contains.
But even if you did that, the compiler would probably still generate warnings unless you made header files that declared those property names to exist.
I agree with most of the answers that NSDictionary should be accessed with objectForKey: or similar methods. However it is possible to allow for dot notation access to a NSDictionary, and for learning purposes this might be interesting for someone. Also when for example your are retrieving large JSON dictionaries via AFNetworking, this method can ease the access and readability of your code.
This is my solution:
DictionaryProperties.h: (class wrapping the NSDictionary for property access)
#interface DictionaryProperties : NSObject{
NSMutableDictionary* _backingDict;
}
#property (nonatomic, strong) NSMutableDictionary* backingDict;
+ (DictionaryProperties*) allocWithDictionary:(NSDictionary*)dict;
#end
DictionaryProperties.m:
#import "DictionaryProperties.h"
#implementation DictionaryProperties
#synthesize backingDict = _backingDict;
- (id) initWithDictionary:(NSDictionary*)dict {
if (self) {
if ([dict isKindOfClass:[NSMutableDictionary class]]) {
self.backingDict = (id)dict;
} else {
self.backingDict = [[NSMutableDictionary alloc] initWithDictionary:dict];
}
}
return self;
}
+ (DictionaryProperties*) allocWithDictionary:(NSDictionary*)dict {
return [[DictionaryProperties alloc] initWithDictionary:dict];
}
- (void)forwardInvocation:(NSInvocation *)invocation
{
NSString* key = NSStringFromSelector(invocation.selector);
invocation.selector = #selector(objectForKey:);
[invocation setArgument:&key atIndex:2];
if ([self.backingDict objectForKey:key]) {
[invocation invokeWithTarget:self.backingDict];
} else {
[self doesNotRecognizeSelector:invocation.selector];
}
}
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector{
return [self.backingDict methodSignatureForSelector:#selector(objectForKey:)];
}
#end
ExampleDictContent.h: (class declaring what is inside the dictionary)
#import "DictionaryProperties.h"
#interface ExampleDictContent : DictionaryProperties
#property (strong, nonatomic) NSString* someData;
#property (strong, nonatomic) NSString* someOtherData;
#end
#implementation ExampleDictContent
#end
Usage: (simple declaration of a dictionary, allocation of wrapper and property access)
#import "ExampleDictContent.h"
NSDictionary* d = [NSDictionary dictionaryWithObjects:NSArray arrayWithObjects:#"someData content", #"someOtherData content", nil
forKeys:NSArray arrayWithObjects:#"someData", #"someOtherData", nil];
ExampleDictContent* dictWProps = [ExampleDictContent allocWithDictionary:d];
NSLog(dictWProps.someData);
NSLog(dictWProps.someData);
This will print:
someData content
someOtherData content
So basically DictionaryProperties works as a facade for accessing the NSDictionary. It uses forwardInvocation to convert a get-property method call into a getObjectForKey: call on the dictionary. What I like about it, is that it allows for autocompletion on the dictionary, and also allows me to explicitly declare what keys I want to access (in the ExampleDictContent.h file). Note that this solution does not allow for write access to the properties, but that can be added as shown in the link below.
This solution has partly been inspired by karstenlitsche's solution. The main difference is that this solution is based on sub-classing instead of categories.
No, I don't think so.
From the reference manual.
Accessing Keys and Values
– allKeys
– allKeysForObject:
– allValues
– getObjects:andKeys:
– objectForKey:
– objectsForKeys:notFoundMarker:
– valueForKey:
That's listed as the only way to access the keys and the values. So you are doing it alright.
You would be able to access it if the keys were a public property and it was readable.
The way that you have mentioned for accessing element of dictionary is ideal way(using keys).
If you want to do something else, might be you can use-
NSArray *allValues = [returnVal allValues];
Now using this array as well you can perform tasks.
And if you want something specific then mention that, might be for that there can be some other way.
Also as NSDictionary class won't have any property defined, so dot notation is directly not possible.
No, you are doing it the correct way. In the iOS world, often the correct way is the only way. :)
If you really want dot notation (and other nice things you get with typed objects), you're going to have to stuff the dictionary representation into an object. Most commonly my interface will look like:
#interface FooBar : NSObject {
NSString *someData;
int someNumber;
}
#property (nonatomic, copy) NSString *someData;
#property (nonatomic, assign) int someNumber;
+ (FooBar *)FooBarFromDictionary:(NSDictionary *)dataDict;
#end
The implementation should be clear. Then you can
FooBar *fb = [FooBar FooBarFromDictionary:data];
NSLog(#"fb.someData = %#", fb.someData);
Technically, you can do something like this:
typedef id (^valueBlock)(id);
#interface NSDictionary(dotNotationAddons)
#property(nonatomic, readonly) valueBlock value;
#end
#implementation NSDictionary(dotNotationAddons)
-(valueBlock) value
{
return [[^(id key) {
return [self objectForKey:key];
} copy] autorelease];
}
#end
int main (int argc, const char * argv[])
{
#autoreleasepool {
NSDictionary *dictionary = [NSDictionary dictionaryWithObjectsAndKeys:#"1", #"One", #"2", #"Two", #"3", #"Three", #"4", #"Four", nil];
id value = dictionary.value(#"One");
NSLog(#"%#", value);
}
return 0;
}
I don't know if that is what you were looking for, but I hope it helps!
The answer's still no, but you can use the shorthand
myDictionary[#"key"]
instead of
[myDictionary objectForKey:#"key"]
In Swift, there is a solution that may not seem very elegant but does the trick.
It will require a typeAlias for each specific type of Dictionary and also an extension with variables (with getter/setter) for each of the expected keys in your dictionary. Not a good practice at all
It may be easier wrap your dict object in an object (class/struct) with the same treatment.
typealias MyDict = [String:AnyObject]
extension MyDict {
var key: AnyObject? {
get { return self["key"] }
set { self["key"] = newValue }
}
}
// Usage
var myDict = MyDict()
// Get the value
myDict["key"] = "value1" as AnyObject
if let str = myDict.key {
print(str) // prints "value1"
}
// Set the value
myDict.key = "value2" as AnyObject
if let str = myDict["key"] {
print(str) // prints "value2"
}

Sorting 2-dimensional arrays

I have the following case. I import data from an xml feed and from facebook graph api, in this case posts. I want to merge this data in a array and sort this on the included date data.
I have now the following:
[containerArray addObject: [NSMutableArray arrayWithObjects: created_time, message, picture, fbSource, nil ]
];
This creates a 2-dimensional array, but i want to order all the entries on created_time.
How can i best solve this problem? Thnx in advance!!
Create a data class containing the necessary instance variables instead of the mutable array. Then you can use the various sort method of the NSArray class, for example sortedArrayUsingDescriptors.
A sort could look like this:
NSSortDescriptor *sortDescriptor = [[[NSSortDescriptor alloc] initWithKey:#"created_time"
ascending:YES] autorelease];
NSArray *sortedArray = [containerArray sortedArrayUsingDescriptors:[NSArray arrayWithObject:sortDescriptor]];
[sortDescriptor release];
EDIT
To quote Mr. Fowler from his book Refactoring: Improving the Design of Existing Code.
Replace Array with Object
You have an array in which certain elements mean different things.
Replace the array with an object that has a field for each element
...
Motivation
Arrays are a common structure for organizing data. However, they should be used only to contain a collection of similar objects in somre order.
That's what we want to do here. Let's create a simple Posts class. You can easily add your custom initializer which accepts the four values as parameters, or even a convenience class method to return an autoreleased object later on. This is just a basic skeleton:
Post.h
#interface Posts : NSObject
{
NSDate *created_time;
NSString *message;
UIImage *picture;
id fbSource; // Don't know what type :)
}
#property (nonatomic, retain) NSDate *created_time;
#property (nonatomic, copy) NSString *message;
#property (nonatomic, retain) UIImage *picture;
#property (nonatomic, retain) id fbSource;
#end
Post.m
#import "Post.h"
#implementation Post
#synthesize created_time, message, picture, fbSource;
#pragma mark -
#pragma mark memory management
- (void)dealloc
{
[created_time release];
[message release];
[picture release];
[fbSource release];
[super dealloc];
}
#pragma mark -
#pragma mark initialization
- (id)init
{
self = [super init];
if (self) {
// do your initialization here
}
return self;
}
EDIT 2
Adding a Post object to your array:
Post *newPost = [[Post alloc] init];
newPost.reated_time = [Date date];
newPost.message = #"a message";
newPost.picture = [UIImage imageNamed:#"mypic.jpg"];
// newPost.fbSource = ???
[containerArray addObject:newPost];
[newPost release];

Need to store array of an array of class objects in View Controller

An iPhone question for you guys! I have an NSURLConnection that downloads XML from a server and processes it into an array which is a part of another array. I have no way of knowing how many objects I will need, so I cannot allocate an NSArray beforehand. My question is:
Would it be better to create the parent array as an NSArray at the class level and allocate it after I store the data in a temporary NSMutableArray or just make the NSMutableArray at class level? It is worth noting that I do not need to modify the array other than to release it at the end of the program run.
I don't think it really matters.
I'm reading the Beginning iPhone 3 Development book at the moment, and usually loading the data is done like this:
You'd have an NSArray property :
#interface
{
...
NSArray *listOfObjects;
...
}
#property (nonatomic, retain) NSArray *listObObjects;
...
Then you create an NSMutableArray, load your data and set the property:
NSMutableArray *array = [[NSMutableArray alloc] init]; // ?
// load the XML into array here
...
self.listOfObjects = array;
[array release];
listOfObjects would then be treated as an NSArray (immutable) although it actually would be an NSMutableArray.
I think what you probably want to do is create some Classes that match what you are representing in your xml. For example if you xml looks something like this:
<peopleList>
<person>
<name>Joe</name>
<possession>Shovel</possession>
</person>
<person>
<name>Sam</name>
<possession>Shovel</possession>
<possession>Fork</possession>
<possession>Backpack</possession>
</person>
</peopleList>
You should have a PeopleList Class and a Person Class. An object instantiated from the PeopleList Class has your first array that contains one or more Person objects. The Person objects, in turn, also have arrays to hold the possesions (which in this case are strings - although if needed they could be Possesion objects) In this case, to help the example the Person Class also has another property: 'name' which is also a String.
For example:
#interface PeopleList {
NSMutableArray *persons; // An array to store the Person objects
}
#property (nonatomic, retain) NSMutableArray *persons;
#end
#interface Person {
NSString *name;
NSMutableArray *possesions; //An array to store this Person's possesion strings
}
#property (nonatomic, retain) NSString *name;
#property (nonatomic, retain) NSMutableArray *possesions;
#end
In the init method's of these objects you will have to alloc/init the arrays so they will be ready to receive objects. And because I alloc'd them, my class is responsible for the release.
#implementation PeopleList
-(id) init {
if (self = [super init]) {
persons = [[NSMutableArray alloc] init];
}
}
-(void) dealloc {
[persons release];
[super dealloc];
}
#end
#implementation PeopleList
-(id) init {
if (self = [super init]) {
possesions = [[NSMutableArray alloc] init];
}
}
-(void) dealloc {
[possesions release];
[super dealloc];
}
#end
Now that this is done, you can set up your data structure of cascading array's.
As you are parsing the XML when you come across a PeopleList Tag do a:
currentPeopleList = [[PeopleList alloc] init];
and when you come across a Person tage do a:
currentPerson = [[Person alloc] init];
[peopleList.persons addObject: person];
a possesion:
[currentPerson.possesion addObject: contentsOfCurrentElement];
or name:
currentPerson.name = contentsOfCurrentElement;
But to answer your more specific question, I not store the data in a temporary NSArray and then copy that into a NSMutableArray. There is almost no performance gain by doing so, and you will burn cpu cycles and memory doing the copy.