SBJSON encode Object who contain an array of another Object - objective-c

i've a little json encode problem :
i need to encode an object format JSON with SBJSON before send it to a php server
At the moment this sample code work :
NSArray *arrayData = [NSArray arrayWithObjects:
user.id == nil ? [NSNumber numberWithInt:-1] : user.id,
ProfessionField.text, NameField.text, RPPSField.text, RPPSField.text,
NameField.text, SurnameField.text, StreetField.text,
TownField.text, CpField.text, MailField.text,
PhoneField.text, FaxField.text, MobileField.text,
// [user.horaires JSONRepresentation],
nil];
NSArray *arrayKey = [NSArray arrayWithObjects:
#"id", #"spe", #"name", #"rpps", #"cip",
#"name", #"surname", #"rue",
#"ville", #"cp", #"mail",
#"tel", #"fax", #"port",
// #"horaires",
nil];
NSDictionary *dataBrut = [NSDictionary dictionaryWithObjects:arrayData forKeys:arrayKey];
NSDictionary *jsonDict = [NSDictionary dictionaryWithObject:dataBrut forKey:#"data"];
NSString *jsonRequest = [jsonDict JSONRepresentation];
The problem is when i need to send the "user.horaires" (here in comment)
Application CRASH at the JSON representation of this object.
this object is an Array of the following class :
#interface Horaire : NSObject
{
BOOL morning;
}
#property (nonatomic, strong) NSNumber *id;
#property (nonatomic, strong) NSString *open;
#property (nonatomic, strong) NSString *close;
Someone know how to succes to encode this ?

You shouldn't be including the JSON representation as a JSON item. JSON does not "escape" string data very well, so the embedded JSON (unless you separately "escape" it) will cause parsing to choke.
Instead you should place the dictionary or array that was used to produce the JSON representation (ie, "user.horaires" itself) in the location where you show the representation being produced and inserted. Then the entire structure will be JSON-encoded in one operation.
Ie:
NSArray *arrayData = [NSArray arrayWithObjects:
user.id == nil ? [NSNumber numberWithInt:-1] : user.id,
ProfessionField.text, NameField.text, RPPSField.text, RPPSField.text,
NameField.text, SurnameField.text, StreetField.text,
TownField.text, CpField.text, MailField.text,
PhoneField.text, FaxField.text, MobileField.text,
user.horaires,
nil];

Related

Creating Array with properties for each item in Objective-c

tell me please, how do I create an array in which each element will have a number of properties. For example:
array:
|
|-item 1 ( property_1-"Name1", property_2-"LastName1", property_3-"Age1");
|-item 2 ( property_1-"Name2", property_2-"LastName2", property_3-"Age2");
|-item 3 ( property_1-"Name3", property_2-"LastName1", property_3-"Age2");
|-…
In this case, the different elements of an array can have one and the same property, such as in the code posted above - "item 3" has the "property 2" is the same as in "item 1", and "property 3" is the same as in "item 2"
Tell me, please, how best to do it and, if not difficult, write a simple example or a link to some tutorial.
Thank you in advance)
There are two methods that I would suggest:
1. Use a class to store all the properties
Here's an example:
#interface Wrapper : NSObject
#property (nonatomic, assign) NSString* property_1;
#property (nonatomic, assign) NSString* property_2;
#property (nonatomic, assign) NSString* property_3;
#end
Then you can use it as a dictionary:
NSString* value=[myWrapperInstance valueForKey: #"property_1"];
But here comes the alternative solution:
1. Use a NSDictionary to store all the properties
NSDictionary* dict= #{ #"property_1" : #"Name1" ,#"property_2" : #"Name2", #"property_3" : #"Name3" };
Then the solution comes easy:
NSMutableArray* objects=[NSMutableArray new];
for(int i=0; i<N; i++)
{
NSDictionary* dict= #{ #"property_1" : #"Name1" ,#"property_2" : #"Name2", #"property_3" : #"Name3" };
[objects addObject: dict];
}
Sounds like you want an NSArray of NSDictionary objects:
NSDictionary *dict1 = [NSDictionary dictionaryWithObjectsAndKeys:#"Name1", #"property_1", #"LastName1", #"property_2", #"Age1", #"property_3", nil);
NSDictionary *dict2 = [NSDictionary dictionaryWithObjectsAndKeys:#"Name2", #"property_1", #"LastName2", #"property_2", #"Age2", #"property_3", nil);
NSDictionary *dict3 = [NSDictionary dictionaryWithObjectsAndKeys:#"Name3", #"property_1", #"LastName3", #"property_2", #"Age3", #"property_3", nil);
NSArray *array = [NSArray arrayWithObjects:dict1, dict2, dict3, nil];
If you want to update this later then you should use NSMutableDictionary and NSMutableArray, respectively.
If you are not using ARC, the you need to release objects when you are finished with them.

SBJsonParser can't parse NSArray

I am trying to parse NSArray to JSON but I get the following error:
* Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[__NSArrayM
JSONRepresentation]: unrecognized selector sent to instance 0xa93e460'
* First throw call stack: (0x21f1012 0x1feae7e 0x227c4bd 0x21e0bbc 0x21e094e 0x3445a 0x33ecc 0x26a453f 0x26b6014 0x26a72e8 0x26a7450
0x95e22e12 0x95e0acca) libc++abi.dylib: terminate called throwing an
exception
I have included all classes from SBJson_3.1.1/Classes directory.
This is code:
NSMutableArray* arr = ...get array
NSString* jsonArr = [arr JSONRepresentation]; // here I get error
When I do this in array of simple strings it works:
NSData *jsonData = [NSJSONSerialization arr
options:NSJSONWritingPrettyPrinted error:nil];
NSString *jsonString = [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding];
But my array contain list of objects (Person) maybe there is a problem.
I use Item instead of person just as example
Item.h
#interface Item : NSObject
{
BOOL IsOpen;
NSString* Description;
}
#property int ItemId;
#property int SequenceId;
#property BOOL IsOpen;
#property NSString* Description;
- (id) proxyForJson;
#end
Item.m
#implementation Item
#synthesize ItemId;
#synthesize SequenceId;
#synthesize Description;
#synthesize IsOpen;
- (id) proxyForJson {
return [NSDictionary dictionaryWithObjectsAndKeys:
[NSString stringWithFormat:#"%i", ItemId], #"ItemId",
SequenceId, #"SequenceId",
Description, #"Description",
IsScanned, #"IsOpen",
nil ];
}
#end
UPDATE
Student example
I tried to make a separate project. I copied to new project all from classes directory of sbjson framework. This is code:
#import "SBJson.h"
#interface Student : NSObject
{
NSString *name;
NSInteger sid;
NSString *email;
}
#property NSString *name;
#property NSInteger sid;
#property NSString *email;
- (id) proxyForJson;
#end
#implementation Student
#synthesize name;
#synthesize sid;
#synthesize email;
- (id) proxyForJson{
return [NSDictionary dictionaryWithObjectsAndKeys:
name, #"student_name",
[NSNumber numberWithInt:sid], #"student_id",
email, #"email",
nil ];
}
#end
NSMutableArray* studentArray = [[NSMutableArray alloc]init];
Student* s1 = [[Student alloc]init];
s1.name = #"student 1";
s1.sid = 45;
s1.email = #"test#test.com";
Student* s2 = [[Student alloc]init];
s2.name = #"student 2";
s2.sid = 46;
s2.email = #"plavi#test.com";
[studentArray addObject:s1];
[studentArray addObject:s2];
NSString *jsonString = [studentArray JSONRepresentation];
NSLog(#"%#", jsonString);
And again I get error:
Terminating app due to uncaught exception
'NSInvalidArgumentException', reason: '-[__NSArrayM
JSONRepresentation]: unrecognized selector sent to instance 0x741b100'
SBJson doesn't support serialising user-defined classes without assistance. If you implement a -proxyForJson method in your Person class (example here) it should work, however.
If you're using a recent Xcode the below should work. Header:
#interface Item : NSObject
#property int ItemId;
#property int SequenceId;
#property BOOL IsOpen;
#property(copy) NSString* Description;
- (id) proxyForJson;
#end
Implementation:
#implementation Item
- (id) proxyForJson {
return #{ #"ItemId": #(self.ItemId),
#"SequenceId": #(self.SequenceId),
#"Description": self.Description,
#"IsOpen": #(self.IsOpen)
};
}
#end
This should let SBJson serialise the Item objects to NSDictionaries. However, SBJson does not support parsing JSON into custom objects. So you will always get this back in the dictionary form. I don't know of any Objective-C JSON parser that provides bindings to custom types.
I would suggest reading the top two comments of this thread. If those don't help, it is still very likely that you are not installing the library correctly. Try removing the SBJSON files from your project and then readding them, making sure that they are added to your target. Also, make sure you are importing the SBJSON header into your class.
I would suggest that you try using JSONRepresentation on an array of NSString objects. If the framework is correctly installed, this should definitely work. This way you can narrow down whether it is an installation issue or whether it is an issue with your custom class.
Check out the following excerpt from Working with JSON in iOS 5 Tutorial
This is mainly for generating JSON.
//build an info object and convert to json
NSDictionary* info = [NSDictionary dictionaryWithObjectsAndKeys:
[loan objectForKey:#"name"],
#"who",
[(NSDictionary*)[loan objectForKey:#"location"]
objectForKey:#"country"],
#"where",
[NSNumber numberWithFloat: outstandingAmount],
#"what",nil];
//convert object to data
NSData* jsonData = [NSJSONSerialization dataWithJSONObject:info options:NSJSONWritingPrettyPrinted error:&error];
Now, the difference lies in using NSDictionary and converting that into JSON Data. Try forming the JSON in the way given above and check if the problem persists.
you are correctly linking the category? to me it kinda looks like you are missing a category

JSONKit with Key-Value Coding and BOOLs

I'm attempting to serialize various objects by using Key-Value Coding to convert them to an NSDictionary, then JSONKit to serialize the NSDictionary to an NSString/NSData. I'm running into problems converting BOOL properties.
The KVC guidelines state that valueForKey: will, for BOOL properties, create an NSNumber via [NSNumber numberWithBool:]. JSONKit states that NSNumbers created via numberWithBool: will be serialized to true/false. I've tested JSONKit's claim and it works. However, when I access a BOOL value with KVC, I get an object which does not look like it was created via numberWithBool:. In particular, it does not evaluate equal to kCFBooleanTrue, which JSONKit uses as a marker for a boolean. The end result is that my BOOL properties are serialized to 0/1 instead of true/false, which is causing problems for the receiving API.
How do I determine if an NSNumber from KVC came from a BOOL property? Am I misreading Apple's documentation? Or is there some other way to get this serialization procedure to work?
Below is the test which is failing:
#import "JSONKit.h"
- (void) testCompareKVCBoolToNumberWithBool {
NSNumber *numberBool = [NSNumber numberWithBool:YES];
//This passes
STAssertTrue(numberBool == (id)kCFBooleanTrue, #"Number %# should kCFBooleanTrue.", numberBool);
TestModel *model = [[TestModel alloc] init];
model.boolProperty = YES;
NSNumber *kvcBool = [model valueForKey:#"boolProperty"];
//This fails
STAssertTrue(kvcBool == (id)kCFBooleanTrue, #"Number %# should be a kCFBooleanTrue.", kvcBool);
NSDictionary *dict = [NSDictionary dictionaryWithObjectsAndKeys:
numberBool, #"numberBool",
kvcBool, #"kvcBool",
nil];
NSString *jsonString = [dict JSONString];
//This yields: jsonString: {"kvcBool":1,"numberBool":true}
NSLog(#"jsonString: %#", jsonString);
}
And here is the TestModel code:
#interface TestModel : NSObject
#property (assign) BOOL boolProperty;
#end
#implementation TestModel
#synthesize boolProperty = _boolProperty;
#end
Thanks!
You may want to checkout my implementation which does this automatically - https://github.com/QBurst/KVCObjectSerializer

IOS Category on NSArray Causes "instance message does not declare a method with selector"

I am trying to use the Category described in this article:
http://iphonedevelopment.blogspot.com/2008/10/shuffling-arrays.html
I have setup the following:
// NSArray+Shuffle.h
#import <Foundation/Foundation.h>
#interface NSArray (Shuffle)
-(NSArray *)shuffledArray;
#end
// NSArray+Shuffle.m
#import "NSArray+Shuffle.h"
#implementation NSArray (Shuffle)
-(NSArray *)shuffledArray
{
NSMutableArray *array = [NSMutableArray arrayWithCapacity:[self count]];
NSMutableArray *copy = [self mutableCopy];
while ([copy count] > 0)
{
int index = arc4random() % [copy count];
id objectToMove = [copy objectAtIndex:index];
[array addObject:objectToMove];
[copy removeObjectAtIndex:index];
}
// Using IOS 5 ARC
// [copy release];
return array;
}
#end
Then in my code that I want to use this, I imported the Category:
#import "NSArray+Shuffle.h"
Then, I attempted to use it like this:
NSArray *orderedGallary = [[NSArray alloc] initWithObjects:
[NSDictionary dictionaryWithObjectsAndKeys:
#"Pic1", #"pageName",
[UIImage imageNamed:#"Pic1.jpg"],#"pageImage",
nil],
[NSDictionary dictionaryWithObjectsAndKeys:
#"Pic2", #"pageName",
[UIImage imageNamed:#"Pic2.jpg"],#"pageImage",
nil],
nil];
NSArray *shuffler = [[NSArray alloc] shuffledArray:orderedGallary];
_pageData = [shuffler shuffledArray:orderedGallary];
But, I get the following compiler error message:
ModelController.m: error: Automatic Reference Counting Issue: Receiver type 'NSArray' for instance message does not declare a method with selector 'shuffledArray:'
Any ideas?
shuffledArray is a method that takes no parameters, it is different from shuffledArray:, which is a method that takes one parameter.
It looks like what you meant was:
NSArray* shuffled = [orderedGallery shuffledArray];
Here you are sending this message to your original array, and it returns a new array that is shuffled.
You're trying too hard. You only need to send -shuffledArray to orderedGallery.
NSArray *orderedGallary = [[NSArray alloc] initWithObjects:
[NSDictionary dictionaryWithObjectsAndKeys:
#"Pic1", #"pageName",
[UIImage imageNamed:#"Pic1.jpg"],#"pageImage",
nil],
[NSDictionary dictionaryWithObjectsAndKeys:
#"Pic2", #"pageName",
[UIImage imageNamed:#"Pic2.jpg"],#"pageImage",
nil],
nil];
_pageData = [orderedGallery shuffledArray];
See how you have declared shuffledArray not to take any arguments? Simply sending this message to any instance of NSArray will return your shuffled array.
shuffledArray does not take a parameter but is called directly on the array:
NSArray *myShuffledArray = [orderedGallery shuffledArray]
You have declared (in the .h) and defined (in the .m) a method named shuffledArray.
You are calling a method named shuffledArray: (notice the colon, which indicates an argument).
You want to be calling
NSArray *shuffled = [orderedGallery shuffledArray];
you don't need the argument because you are sending the method to the ordered array.
(There's no object that's actually a "shuffler" - independent of the array - so I wouldn't use that name as the variable name. The array is shuffling a copy of itself and returning the new shuffled array.)

Can't save to plist

I have my own object class from inherited from NSObject
#interface BlockedCell : NSObject
{
NSValue *gridValue;
NSString *name;
}
#property (nonatomic, retain) NSValue *gridValue;
#property (nonatomic, retain) NSString *name;
#end
So I try to create a few objects:
NSMutableDictionary *dict = [[dict alloc] init];
for (int i = 0; i < 5; ++i)
{
BlockedCell *block = [[BlockedCell alloc] init];
block.gridValue = [NSValue valueWithCGPoint: CGPointMake(0.0f, 0.0f)];
block.name = #"something";
[dict setObject: block forKey: [NSString stringWithFormat: #"item_%d", i]];
[block release];
}
if([dict writeToFile: path atomic: YES])
NSLog(#"Saved");
else
NSLog(#"Failed to save");
[dict release];
And what I get for the output is "Failed to save"..
If my dictionary does not contains any data, then it will output "Saved"
EDIT:
After I did more testing, I found out that actually is the NSValue causing the saving failed.
So what should I do if I want to save CGPoint into plist?
As you discovered, property lists cannot store NSValue objects directly. The supported classes are NSData, NSString, NSArray, NSDictionary, NSDate, and NSNumber, as documented in the NSPropertyListSerialization Class Reference.
The easiest workaround would be to use NSString instead of NSValue:
block.gridString = NSStringFromCGPoint(CGPointZero);
CGPoint point = CGPointFromString(block.gridString);
you cant save NSValue directly.In your case you have to save a point in the form ofstring use below line
CGPoint point = CGPointMake(10.0,20.0)
//you need to translate the point into a compatible "Plist" object such as NSString
//luckily, there is a method for that
[rectArray addObject:NSStringFromPoint(point)];
//save into a plist
.....
on retrieval of this value
CGPoint Point = CGPointFromString([rectArray objectAtIndex:0]);