Convert an object into Json using SBJson or other JSON library - iphone

I need a easy to use library whit examples for converting NSObjects to JSON and back again, I found a ton of parseing examples on the net for parsing JSon but not too much on converting NSObject to JSON using SBJSON, Anybody body have a good tutorial or a sample code to convert NSObject to JSON ?

With SBJSON, it's really simple.
NSString *myDictInJSON = [myDict JSONRepresentation];
NSString *myArrayInJSON = [myArray JSONRepresentation];
Of course, to go the other way array, do:
NSDictionary *myDict = [myDictInJSON JSONValue];
NSArray *myArray = [myArrayInJSON JSONValue];

Using SBJson, to convert a object to JSON string, you have to override the proxyForJson method. Like the following,
The .h file,
#interface MyCustomObject : NSObject {
NSString *receiverFirstName;
NSString *receiverMiddleInitial;
NSString *receiverLastName;
NSString *receiverLastName2;
}
#property (nonatomic, retain) NSString *receiverFirstName;
#property (nonatomic, retain) NSString *receiverMiddleInitial;
#property (nonatomic, retain) NSString *receiverLastName;
#property (nonatomic, retain) NSString *receiverLastName2;
- (id) proxyForJson;
- (int) parseResponse :(NSDictionary *) receivedObjects;
}
In the implementation file,
- (id) proxyForJson {
return [NSDictionary dictionaryWithObjectsAndKeys:
receiverFirstName, #"ReceiverFirstName",
receiverMiddleInitial, #"ReceiverMiddleInitial",
receiverLastName, #"ReceiverLastName",
receiverLastName2, #"ReceiverLastName2",
nil ];
}
And to get the object from the JSON string you have to write a parseResponse method like this,
- (int) parseResponse :(NSDictionary *) receivedObjects {
self.receiverFirstName = (NSString *) [receivedObjects objectForKey:#"ReceiverFirstName"];
self.receiverLastName = (NSString *) [receivedObjects objectForKey:#"ReceiverLastName"];
/* middleInitial and lastname2 are not required field. So server may return null value which
eventually JSON parser return NSNull. Which is unrecognizable by most of the UI and functions.
So, convert it to empty string. */
NSString *middleName = (NSString *) [receivedObjects objectForKey:#"ReceiverMiddleInitial"];
if ((NSNull *) middleName == [NSNull null]) {
self.receiverMiddleInitial = #"";
} else {
self.receiverMiddleInitial = middleName;
}
NSString *lastName2 = (NSString *) [receivedObjects objectForKey:#"ReceiverLastName2"];
if ((NSNull *) lastName2 == [NSNull null]) {
self.receiverLastName2 = #"";
} else {
self.receiverLastName2 = lastName2;
}
return 0;
}

From JSON String to Objects:
SBJsonParser *parser = [[SBJsonParser alloc] init];
// gives array as output
id objectArray = [parser objectWithString:#"[1,2,3]"];
// gives dictionary as output
id objectDictionary = [parser objectWithString:#"{\"name\":\"xyz\",\"email\":\"xyz#email.com\"}"];
From Objects to JSON String:
SBJsonWriter *writer = [[SBJsonWriter alloc] init];
id *objectArray = [NSArray arrayWithObjects:#"Hello",#"World", nil];
// Pass an Array or Dictionary object.
id *jsonString = [writer stringWithObject:objectArray];

Related

How to add NSDictionary of dictionaries to NSArray?

I have a dictionary of dictionaries. This is the structure of my main dictionary:
The content of dictionary(
{
mondaysSales= {
totalSale = "1234.99";
},
tusdaySales= {
totalSale = "1234.99";
},
wednesdaySale={
totalSale = "1234.99";
},
thursdaySale{
totalSale = "1234.99";
},
fridaySale{
totalSale = "1234.99";
}
)
but I want to add each day with the day key to a array. For example:
this would be one of the entries of the array:
fridaySale{
totalSale = "1234.99";
}
Any of you how can accomplish this?, I'll really appreciate your help.
You can loop through the dictionary and add it to the array. Note that dictionaries are not sorted and you probably won't end up with a correct order for your weekdays
NSMutableArray *array = [#[] mutableCopy]
for (NSString* key in dictionary) {
id value = [dictionary objectForKey:key];
[array addObject:value];
}
Why not create a new object type and add that to the array?
StorageObject.h:
#interface StorageObject:NSObject
#property (nonatomic, retain) NSString *day;
#property (nonatomic, retain) NSString *saleType;
#property (nonatomic, retain) NSNumber *saleValue;
#end
StorageObject.m:
#implementation StorageObject
#synthesize
day = _day,
saleType = _saleType,
saleValue = _saleValue;
- (void)dealloc
{
[_day release];
[_saleType release];
[_saleValue release];
[super dealloc];
}
#end
Now just loop through your NSDictionary using:
for(NSString *key in [dictionary1 allKeys])
{
NSDictionary *innerDictionary = [dictionary1 objectForKey:key];
}
For every dictionary returned in that loop, instantiate your custom storage object and add it to the array.
I figure out solution:
NSMutableDictionary *tempDict=[[NSMutableDictionary alloc]init];
[tempDict setObject:[mainDic objectForKey:key] forKey:key];
[myArray addObject:tempDict];
Assuming you have a dictionary of dictionaries:
You can do
NSMutableArray *list = [#[] mutableCopy];
for (NSString *key in [mainDictionary allKeys]) {
NSDictionary *dict = #{
key : [mainDictionary objectForKey:key],
};
[list addObject:dict];
}
There is an easy way to do this just don't create an NSDictionary and instead create an NSArray, by doing the following
NSArray *Array = #[#{#"friday sale, totalSale" : #"1234.99"}]
Or if you want to have specifics gotten from anything else in it.
NSInteger value = 1;
NSArray *Array = #[#{ #"Friday sale, totalSale" : [NSString stringWithFormat:#"%ld", (long)value]}];
Then you get this value from simply saying,
NSString *somestring = Array["Friday sale, totalSale"];

Make a clean NSString of words from a NSMutableArray of word objects, then use for search

suitsArray is MutableArray with objects (words) added when buttons are in selected state.
In the following piece, I need to make suitsCriteriaString a clean string with only a space separating the words from suitsArray, meaning no comma or other symbols! (with NSSet or something?)
NSString *suitsCriteriaString = [NSString stringWithFormat:#"%#", suitsArray];
NSString *wineSuitsString = [wine objectForKey:#"Suits"];
NSRange range = [wineSuitsString rangeOfString:suitsCriteriaString options:NSCaseInsensitiveSearch];
Then, the NSRange, if it's done right, should check if the words in suitsCriteriaString, are existing in wineSuitsString. Then, the results should be filtered to containing only the matching words!
Codes for my search function follows, let me know if you need some more info to make this work.
SearchViewController.h
#import <UIKit/UIKit.h>
#interface SearchViewController : UIViewController {
#property (nonatomic, strong) NSMutableArray *allObjectsArray;
#property (nonatomic, strong) NSMutableArray *resultObjectsArray;
#property (nonatomic, strong) NSMutableArray *suitsArray;
#property (nonatomic, retain) IBOutlet UISlider *minPrisSlider;
#property (nonatomic, retain) IBOutlet UISlider *maxPrisSlider;
-(IBAction)searchButtonPressed:(id)sender;
#end
SearchViewController.m:
Fill allObjectsArray:
- (void)viewDidLoad
{
[super viewDidLoad];
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *path = [documentsDirectory stringByAppendingPathComponent:#"Wine.plist"];
allObjectsArray = [[NSMutableArray alloc] initWithContentsOfFile:path];
}
Add or remove object from suitsArray, I have 10 buttons similar to this:
-(IBAction)lambButtonPressed:(id)sender
{
if (lambButtonSelected == 0) {
lambButtonSelected = 1;
[suitsArray addObject:#"lamb"];
} else {
lambButtonSelected = 0;
[suitsArray removeObject:#"lamb"];
}
}
Then, add objects to search results:
-(IBAction)searchButtonPressed:(id)sender{
resultObjectsArray = [NSMutableArray array];
for(NSDictionary *wine in allObjectsArray)
{
//String for price
NSString *winePrice = [wine objectForKey:#"Price"];
/*THIS IS WHERE I'M TRYING TO CHECK IF THE WORDS IN suitsCriteriaString
ARE EXISTING IN wineSuitsString */
NSString *suitsCriteriaString = [NSString stringWithFormat:#"%#", suitsArray];
NSString *wineSuitsString = [wine objectForKey:#"Suits"];
NSRange range = [wineSuitsString rangeOfString:suitsCriteriaString options:NSCaseInsensitiveSearch];
//THEN LAST, ADD OBJECTS WITH MATCH OF PRICE CRITERIA AND SUITS CRITERIA TO RESULTS:
BOOL priceConditionGood = YES;
if (minPrisSlider.value <= maxPrisSlider.value && (winePrice.floatValue < minPrisSlider.value || winePrice.floatValue > maxPrisSlider.value))
priceConditionGood = NO;
if (range.location != NSNotFound && priceConditionGood)
[resultObjectsArray addObject:wine];
}
}
//AND PUSH RESULTS CONTROLLER:
ResultsTableViewController *nextController = [[self storyboard] instantiateViewControllerWithIdentifier:#"ResultsController"];
nextController.objectsArray = [[NSMutableArray alloc]initWithArray:resultObjectsArray];
[self.navigationController pushViewController:nextController animated:YES];
}
"suitsCriteriaString" is not going to give you what you want. It's going to be a whole lot easier to just iterate over "suitsArray" and compare each word:
NSString *wineSuitsString = [wine objectForKey:#"Suits"];
BOOL foundMatch = true;
for (NSString *suit in suitsArray) {
NSRange range = [wineSuitsString rangeOfString:suit options:NSCaseInsensitiveSearch];
if (range.location == NSNotFound) {
foundMatch = false;
break;
}
}
You can do that using the componentsJoinedByString: method like that:
NSString *suitsCriteriaString = [suitsArray componentsJoinedByString:#" "];
NSString *wineSuitsString = [wine objectForKey:#"Suits"];
NSRange range = [wineSuitsString rangeOfString:suitsCriteriaString options:NSCaseInsensitiveSearch];

Having problems with Array

SO here's my setup. I have an object called radiostations where I have several strings like callsign, frequency declared and an NSMutableArray called amStationInfo. On my viewcontroller, I access an SQLite database which populates the an array like so...
radiostations.h
#interface radiostations : NSObject {
NSString *format;
NSString *city;
}
#property (nonatomic, retain) NSString *format;
#property (nonatomic, retain) NSString *city;
ViewController.m
radiostations *amStationClass = [[radiostations alloc] init];
NSMutableArray* amStationInfo = [[NSMutableArray alloc] init];
while (sqlite3_step(statement) == SQLITE_ROW)
{
NSString *cityField = [[NSString alloc] initWithUTF8String:
(const char *) sqlite3_column_text(statement, 10)];
NSString *formatField = [[NSString alloc] initWithUTF8String:
(const char *) sqlite3_column_text(statement, 0)];
[amStationInfo addObject:amStationClass];
[amStationClass setCity:cityField];
[amStationClass setFormat:formatField];
}
[tabView reloadData];
sqlite3_finalize(statement);
and then I populate a UITableView
NSString *cityValue = [(radiostations *)[amStationInfo objectAtIndex:indexPath.row] city];
NSString *formatValue = [(radiostations *)[amStationInfo objectAtIndex:indexPath.row] format];
cityLabel.text = cityValue;
formatLabel.text = formatValue;
Initially I was dealing with a few Arrays and this worked just fine. I then changed it so that I was only dealing with one array using a class object and now it's not working. I know the SQLite query and what not works so Im not having any problems with that. It seems as though the array does not get populated.
You are changing the properties of the same radiostations object and adding it over and over again to the array. You need to create a new radiostations object for each row from your sqlite database and add this:
while (...) {
// fetch data as before
radiostations *record = [[radiostations alloc] init];
[record setCity: cityField];
[record setFormat: formatField];
[amStationInfo addObject: record];
[record release];
}
If you are using ARC you need to remove the line [record release];, otherwise it is necessary to avoid leaking those objects.
where did you allocate/init your mutablearray?
something like:
NSMutableArray* amStationInfo = [[NSMutableArray alloc] init];
you need to allocate it once, before to add objects in it

Message Sent to Deallocated Instance

I'm using TouchXML to parse an element in iOS. I retrieve a response from a web service using an NSInvocationOperation, then parse and display the results. Everything works fine as the background thread displays results on the main thread using [self performSelectorOnMainThread:#selector(displayLoginresult:) withObject:res waitUntilDone:NO]; but then I get an error:
2011-07-18 11:58:06.108 billsApp[873:7107] *** -[CFString release]: message sent to deallocated instance 0x5d809b0
The code to parse the element is:
-(LoginResult *) tryLogin:(NSString *)userName withPassword:(NSString*)password{
NSURL *url = [UrlUtility TryLogin:userName passwordHash:password];
CXMLDocument *responseObj = [UrlUtility xmlDocWithUrl:url];
if(responseObj == [NSNull null])
return [NSNull null];
CXMLElement *eleUser = [responseObj nodeForXPath:#"//User" error:nil];
CXMLElement *eleResult = [responseObj nodeForXPath:#"//Result" error:nil];
LoginResultType resultType;
//NSLog(#"Result: ");
//NSLog(eleResult );
// NSLog([[eleResult stringValue] lowercaseString]);
if ([[[eleResult stringValue] lowercaseString ] isEqualToString: #"successful"]){
resultType = Successful;
} else {
resultType = InvalidUsernameOrPassword;
}
LoginResult *res = [[LoginResult alloc] init];
res.result = resultType;
for (CXMLElement *resultElement in [responseObj children] ) {
NSLog([NSString stringWithFormat:#"%# %#", [resultElement name], [resultElement stringValue]]);
}
//todo: fix enum parsing =[LoginResult loginResultTypeStringToEnum: [eleResult stringValue]];
if(eleUser != nil) {
CXMLElement *eleClientID = [eleUser nodeForXPath:#"ClientID" error:nil];
CXMLElement *eleCompanyName = [eleUser nodeForXPath:#"CompanyName" error:nil];
CXMLElement *eleCompanyContact = [eleUser nodeForXPath:#"CompanyContact" error:nil];
CXMLElement *eleIsAgent = [eleUser nodeForXPath:#"IsAgent" error:nil];
CXMLElement *eleParentID = [eleUser nodeForXPath:#"ParentID" error:nil];
NSInteger *clientId = [[eleClientID stringValue] integerValue];
NSString *companyName = [eleCompanyName stringValue];
NSString *companyContact = [eleCompanyContact stringValue];
bool isAgent = [Utils stringToBool:[eleIsAgent stringValue]];
NSInteger *parentId = [[eleParentID stringValue] integerValue];
User *user = [[User alloc] initWithData:clientId companyName:companyName companyContact:companyContact isAgent:isAgent parentId:parentId];
res.user = user;
// release elements
// [eleClientID release];
// [eleCompanyName release];
// [eleCompanyContact release];
// [eleIsAgent release];
// [eleParentID release];
//release raw values
// [companyName release];
// [companyContact release];
}
// [eleResult release];
// [eleUser release];
return res;
}
Part of me wants to say it's a bug with TouchXML, but I find that very unlikely. Is there any way to further track down the error?
EDIT: The definitions for the properties on the User class is:
#property (nonatomic, readwrite) NSInteger clientId;
#property (nonatomic, retain) NSString *companyName;
#property (nonatomic, retain) NSString *companyContact;
#property (nonatomic, readwrite) bool isAgent;
#property (nonatomic, readwrite) NSInteger parentId;
And the instance is initialized with:
-(User*)initWithData:(NSInteger *)clientId companyName:(NSString *)company companyContact:(NSString*)contact isAgent:(bool)agent parentId:(NSInteger*)parentId {
//[self = super init];
self.clientId= clientId;
self.companyName= company;
self.companyContact= contact;
self.isAgent = agent;
self.parentId = parentId;
return self;
}
And the LoginResult class is:
#interface LoginResult : NSObject {
LoginResultType result;
User *user;
NSString * const loginResultTypeArray[4];
}
#property (nonatomic, readwrite) LoginResultType result;
#property (nonatomic, retain) User *user;
Just a try: are you correctly retaining companyName and companyContatct in your User class?
EDIT:
Next thing I would check is loginResultTypeArray. How are string assigned to it? I guess that this advice will also sound trivial to you, but it is really difficult to come up with useful suggestion with so little code...
Can't you get some idea about which CFString is actually being released? If it is not an autoreleased object, possibly the stack trace could point at the method which is sending the release message... this would be very helpful...
Otherwise, I would try and NSLog some of your NSStrings addresses, so that you can compare them with the address you find in the error log (and, again, try and find out which string was actually reused after deallocation).
Finally, another approach to find out which string is used after deletion could be using method swizzling to replace NSString's dealloc with a method of yours that, before calling the swizzled dealloc, does some logging of the objec. This will produce much log info, but knowing the address of the string you could find easily what you need. Find here info about swizzling.
This was a nightmare to track down. I had a method which returned an NSString *, which was then parsed by another method to produce an XML document, then release by the second method. I actually needed to autorelease it in the first method.

return a static const []

So in my model I have the following code... I am successfully able to return each individual value. I want to know how am I able to return the entire speakerTable []... Maybe some advice. Thanks!
typedef struct {
NSUInteger speakerID;
NSString * speakerName;
NSString * speakerPosition;
NSString * speakerCompany;
} SpeakerEntry;
static const SpeakerEntry speakerTable [] =
{
{0, #"name", #"position", #"company"},
{1, #"name", #"position", #"company"},
{-1, nil, nil, nil}
};
This works correctly...
-(NSString *) stringSpeakerCompanyForId:(NSUInteger) identifier{
NSString * returnString = nil;
if ([self helpCount] > identifier) {
returnString = speakerTable[identifier].speakerCompany;
}
return returnString;
}
This does not work at all..
-(id) getSpeaker{
//if ([speakerTable[0].speakerName isKindOfClass:[NSString class]])
// NSLog(#"YES");
NSArray * myArray3 = [NSArray arrayWithArray:speakerTable];
return myArray3;
}
arrayWithArray expects an NSArray, not a C array.
The first one works because you are using it like a C array.
Alternatively - don't use a struct, use an object instead:
Create a class called Speaker.
In Speaker.h
#interface Speaker : NSObject {}
#property (nonatomic, assign) NSUinteger id;
#property (nonatomic, copy) NSString name;
#property (nonatomic, copy) NSString position;
#property (nonatomic, copy) NSString company;
- (void)initWithId:(NSUInteger)anId name:(NSString *)aName position:(NSString *)aPosition company:(NSString *)aCompany;
#end
in Speaker.m
#import "Speaker.h"
#implementation Speaker
#synthesize id, name, position, company;
- (void)initWithId:(NSUInteger)anId name:(NSString *)aName position:(NSString *)aPosition company:(NSString *)aCompany {
if (!([super init])) {
return nil;
}
id = anId;
NSString name = [[NSString alloc] initWithString:aName];
NSString position = [[NSString alloc] initWithString:aPosition];
NSString company = [[NSString alloc] initWithString:aCompany];
return self;
}
- (void)dealloc {
[name release];
[position release];
[company release];
[super dealloc];
}
#end
And now in your calling code you can create an immutable array of speakers with:
Speaker *speaker0 = [[Speaker alloc] initWithId:0 name:#"name0" position:#"position0" company:#"company0"];
Speaker *speaker1 = [[Speaker alloc] initWithId:1 name:#"name1" position:#"position1" company:#"company1"];
Speaker *speakerNull = [[Speaker alloc] initWithId:-1 name:nil position:nil company:nil];
NSArray *speakerArray [[NSArray arrayWithObjects: speaker0, speaker1, speakerNull] retain]
[speaker0 release];
[speaker1 release];
[speakerNull release];
note: this is typed straight in, so feel free to mention/correct typos or errors
The method arrayWithArray takes in an NSArray as an argument, not a C array.