I'm doing some testing on AddressBook and though I can get the data I am looking for I'm curious about how it is delivered. Specifically CFDictionaryRef. When I cast this to a NSDictionary and log to console I just get a string value, not a key=value pair. If I try to log allKeys my test app breaks.
Here's the code snippet I'm using:
if ([contactName isEqualToString:ownerName]) {
//get reference to their email addresses
ABMultiValueRef contactEmails = ABRecordCopyValue(thisPerson, kABPersonEmailProperty);
//loop
for (NSUInteger e = 0; e < ABMultiValueGetCount(contactEmails); e++){
CFDictionaryRef thisPersonCFEmailDict = ABMultiValueCopyValueAtIndex(contactEmails, e);
NSDictionary* thisPersonEmailDict = (__bridge NSDictionary*)thisPersonCFEmailDict;
NSLog(#"%#", [thisPersonEmailDict allKeys]);
}
}
You're assuming ABMultiValueCopyValueAtIndex() returns a CFDictionaryRef. It doesn't. It returns whatever value is appropriate for that multi-value, as evidenced by the CFTypeRef return value.
You can use ABMultiValueGetPropertyType() to find out what type of value a particular multi-value record has. If that returns kABDictionaryPropertyType then you know it's a dictionary, but in your case it's probably returning kABStringPropertyType. It could also return kABInvalidPropertyType, which may indicate that the multi-value contains different types. If so, you'll need to resort to using CFGetTypeID() to identify the type of value returned from ABMultiValueCopyValueAtIndex().
CFDictionaryRef is the Core Foundation counterpart of NSDictionary. Because NSDictionary and CFDictionary are “toll-free bridged,” you can substitute a CFDictionary object for a NSDictionary object in your code (with appropriate casting). Although they are corresponding types, CFDictionary and NSDictionary do not have identical interfaces or implementations, and you can sometimes do things with CFDictionary that you cannot easily do with NSDictionary. Toll-free bridging, means that you can use the same data type as the parameter to a Core Foundation function call or as the receiver of an Objective-C message.
Related
The following code to conditionally concatenate strings for a dictionary seems to work up to the point where I try to place the concatenated result in the dictionary. Can anyone see the error?
NSDictionary *jsonDictionary;
NSString* dictString = #"#\"first\":first,#\"last"
NSString *dictString2=dictString;
if (date.length>0&&![date isKindOfClass:[NSNull class]]) {
//only include this key value pair if the value is not missing
dictString2 = [NSString stringWithFormat:#"%#%s", dictString, "#\"date\":date"];
}
jsonDictionary = #{dictString2}; //syntax error. Says expected colon but that does not fix anything
The syntax for creating an NSDictionary using object literals is:
dictionary = #{key:value}
(and optionally, it can contain multiple key/value pairs separated by commas, but never mind that right now.)
Where "key" and "value" are both NSObjects.
Your line that is throwing the error only contains 1 thing. The contents of a the string in dictString2 has nothing to do with it.
It looks to me like you are trying to build a JSON string manually. Don't do that. Use NSJSONSerialization. That class has a method dataWithJSONObject that takes an NSObject as input and returns NSData containing the JSON string. That's how you should be creating JSON output.
Creating an NSDictionary with values that may be null:
NSDictionary *dict = #{
#"key" : value ?: [NSNull null],
};
When serializing a dictionary, NSNulls are translated to null in the JSON.
If you want to exclude such keys completely, instead of having them with a null value, you'll have to do more work. The simplest is to use an NSMutableDictionary and test each value before adding it.
I had a NSDictionary contains 2 key/value pairs:
NSDictionary *dic = #{#"tag":#2, //NSNumber
#"string":#"someString"}; //NSString
NSLog(#"%i",(int)[dic objectForKey:#"tag"]); //print out 34
NSLog(#"%i",[dic objectForKey:#"tag"] intValue]); //print out 2
Why does "converting id value to int with (int)"get me the wrong result but not the other way? are they in different levels of conversion?
Why does "converting id value to int with (int)"get me the wrong result but not the other way? are they in different levels of conversion?
id is a pointer type. id pointers point to Objective-C objects in memory. By casting id to (int), you are merely reinterpreting (some of) the pointer's bit pattern as an int, which is quite meaningless. You have to call the proper conversion methods of NSString and NSNumber if you want to reliably get the primitive values out of the Objective-C object.
If you ever seemingly get the "correct" value of 2 in the case of pointer-casting with NSNumber, that may be because the Objective-C runtime makes use of an optimization technique called tagged pointers, whereby small objects are not really created and allocated, but their semantics (the number's bits which the NSNumber object stores) is stuffed into the unused bits of the pointer.
#2 is not an int but a NSNumber you can't cast an NSNumber into an int. You have to use intValue method to get the correct result.
The method objectForKey: returns a pointer to the NSNumber object #2, not the value stored in the object itself. So you're typecasting the pointer, not the value 2. In the last line you don't typecast the object but you access a property called intValue which returns the value of the object expressed as an int.
NSDictionary contains Object with Key value pairs,but you passed int(#2) into object
NSDictionary *dic = #{#"tag":#2, //NSNumber
#"string":#"someString"};
so Change int to NSNumber like
NSDictionary *dic = #{#"tag":[NSNumber numberWithInt:2];,#"string":#"someString"};
and you can get it..
int number = [[dict objectForKey:#"tag"] intValue];
for (NSDictionary *fbDictionary in self.latestReactionsArray) {
LatestReaction *latestReaction = [[LatestReaction alloc]init];
NSDictionary *subFBDictionary = [fbDictionary objectForKey:#"participant"];
NSString *facebookUserID = [subFBDictionary valueForKey:#"facebook_id"];
NSNumber* reactionIDNum = [fbDictionary valueForKey:#"reaction_id"];
int reactionID = [reactionIDNum intValue];
NSLog(#"what is name%# and %# and %d",facebookUserID, self.latestReactionsArray,reactionID);
}
I want to save all [fbDictionary valueForKey:#"reaction_id"] in an array or dictionary. How do I do this? Thanks.
Try this:
NSArray *reactionIDs = [self.latestReactionsArray valueForKey:#"reaction_id"];
That will give you an array of reaction IDs.
The reflection in Objective C is not powerful enough to get a usable list of properties that you want to map. Instead, you should implement a class method that returns a list of properties you want to map to JSON and use that.
Lastly, a common "Gotcha" is trying to add nil to a dictionary. You'll need to do a conversion from nil to [NSNull null] and back for the conversion to work properly.
I am creating some XML in objective C, I know how to do it however there is the possibility that there could be 800+ values I might be putting into XML, which I am getting from a NSArray of NSDictionaries.
So I was wondering if there is an efficient way of checking for nill or null in a keyvalue that's of type NSString.
Currently this is what my code looks like:
NSMutableArray *xmlItems = [coreDataController readInstallForXML:selectedInstallID];
for (int i = 0; i < [xmlItems count]; i++) {
NSDictionary *currentXMLItem = [xmlItems objectAtIndex:i];
[xmlWriter writeStartElement:#"Items"];
[xmlWriter writeAttribute:#"insID" value:[currentXMLItem valueForKey:#"insID"]];
// there are about another 20 attributes I have to add here.
}
// then write end elemtent etc.
In the code above I have no added any checking but I was hoping someone might have something better for me than adding a bunch of if statements for each attribute.
You can use [NSDictionary allKeysForObject:] to get all keys for the 'nil' values, so you have a list of keys to ignore.
Generating 800 items is not necessarily 'much' or 'slow'. You don't want to do that on the main thread anyway, so just make sure you perform it as a background operation.
use the allKeys method on the NSDictionary to return an NSArray of keys; then iterate through that array and for each key retrieve the value from the dictionary and use one if statement to check the string before writing out the xml element
Im getting a response from twitter in the form of a string,
What I need is to send the parts where is a comment to an array,
here an example of the string
[{"geo":null,"coordinates":null,"retweeted":false,...
"text":"#KristinaKlp saluditos y besos d colores!"},{"geo":null,"coordinates...
so what I really need are the posts after "text":" =
#KristinaKlp saluditos y besos d colores!
So, how can I take the string and parse it so I get all the messages in an array hopefully?
Thanks a lot!
I haven't done JSON parsing myself in an iOS App, but you should be able to use a library like the json-framework. This library will allow you to easily parse JSON and generate json from dictionaries / arrays (that's really all JSON is composed of).
SBJson docs:
JSON is mapped to Objective-C types in the following way:
null -> NSNull
string -> NSString
array -> NSMutableArray
object -> NSMutableDictionary
true -> NSNumber's -numberWithBool:YES
false -> NSNumber's -numberWithBool:NO
integer up to 19 digits -> NSNumber's -numberWithLongLong:
all other numbers -> NSDecimalNumber
Since Objective-C doesn't have a dedicated class for boolean values,
these turns into NSNumber instances. However, since these are
initialised with the -initWithBool: method they round-trip back to JSON
properly. In other words, they won't silently suddenly become 0 or 1;
they'll be represented as 'true' and 'false' again.
As an optimisation integers up to 19 digits in length (the max length
for signed long long integers) turn into NSNumber instances, while
complex ones turn into NSDecimalNumber instances. We can thus avoid any
loss of precision as JSON allows ridiculously large numbers.
#page objc2json Objective-C to JSON
Objective-C types are mapped to JSON types in the following way:
NSNull -> null
NSString -> string
NSArray -> array
NSDictionary -> object
NSNumber's -initWithBool:YES -> true
NSNumber's -initWithBool:NO -> false
NSNumber -> number
#note In JSON the keys of an object must be strings. NSDictionary
keys need not be, but attempting to convert an NSDictionary with
non-string keys into JSON will throw an exception.
NSNumber instances created with the -numberWithBool: method are
converted into the JSON boolean "true" and "false" values, and vice
versa. Any other NSNumber instances are converted to a JSON number the
way you would expect.
Tutorials
Are there any tutorials? Yes! These are all tutorials provided by
third-party people:
JSON Framework for iPhone - a Flickr tutorial in three parts by John
Muchow. JSON Over HTTP On The iPhone - by Dan Grigsby. AS3 to Cocoa touch: JSON by Andy Jacobs.
There are other libraries you can check out as well like TouchJSON, JSONKit, Yet Another JSON Library
NSJSONSerialization does the job of converting your JSON data into usable data structures as NSDictionary or NSArray very well. I recommend it, even more because it is part of the Cocoa public interface and it is maintained by Apple.
However, if you want to map the content of your JSON to your Objective-C objects, you will have to map each attribute from the NSDictionary/NSArray to your object property. This might be a bit painful if your objects have many attributes.
In order to automatise the process, I recommend you to use the Motis category (personal project) on NSObject to accomplish it, thus it is very lightweight and flexible. You can read how to use it in this post. But just to show you, you just need to define a dictionary with the mapping of your JSON object attributes to your Objective-C object properties names in your NSObject subclasses:
- (NSDictionary*)mjz_motisMapping
{
return #{#"json_attribute_key_1" : #"class_property_name_1",
#"json_attribute_key_2" : #"class_property_name_2",
...
#"json_attribute_key_N" : #"class_property_name_N",
};
}
and then perform the parsing by doing:
- (void)parseTest
{
NSData *data = jsonData; // <-- YOUR JSON data
// Converting JSON data into NSArray (your data sample is an array)
NSError *error = nil;
NSArray *jsonArray = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableContainers error:&error];
if (error)
return; // <--- If error abort.
// Iterating over raw objects and creating model instances
NSMutableArray *parsedObjects = [NSMutableArray array];
for (NSDictionary *rawObject in jsonArray)
{
// Creating an instance of your class
MyClass instance = [[MyClass alloc] init];
// Parsing and setting the values of the JSON object
[instance mjz_setValuesForKeysWithDictionary:rawObject];
[parsedObjects addObject:instance];
}
// "parseObjects" is an array with your parsed JSON.
// Do whatever you want with it here.
}
The setting of the properties from the dictionary is done via KeyValueCoding (KVC) and you can validate each attribute before setting it via KVC validation.
I recently had to do this. After looking at the various options out there, I threw JSONKit into my app (I found it on a JSON discussion on StackOverflow). Why?
A) It is VERY VERY simple. I mean, all it has is the basic parsing/emitting functions, what more do you need?
B) It is VERY VERY fast. No overhead - just get the job done.
I should note, I had never done JSON before - only heard of the term and didn't even know how to spell it. I went from nothing, to a working app, in about 1 hour. You just add one class to your app (the .h, .m), instantiate it, and call the parser to a dictionary object. Voila. If it contains an array, you just get the objectForKey, cast it as an NSArray. It's really hard to get simpler than that, and very fast.
For a good comparison of the speed of the different libraries for JSON parsing on iOS, take a look at The Ultimate Showdown.
-(IBAction)btn_parse_webserivce_click:(id)sender
{
// Take Webservice URL in string.
NSString *Webservice_url = self.txt_webservice_url.text;
NSLog(#"URL %#",Webservice_url);
// Create NSURL from string.
NSURL *Final_Url = [NSURL URLWithString:Webservice_url];
// Get NSData from Final_Url
NSData* data = [NSData dataWithContentsOfURL:
Final_Url];
//parse out the json data
NSError* error;
// Use NSJSONSerialization class method. which converts NSData to Foundation object.
NSDictionary* json = [NSJSONSerialization
JSONObjectWithData:data
options:kNilOptions
error:&error];
// Create Array
NSArray* Response_array = [json objectForKey:#"loans"];
NSLog(#"Array: %#", Response_array);
// Set Response_array to textview.
self.txt_webservice_response.text = [NSString stringWithFormat:#"%#"
,Response_array];
}
How about NSJSONSerialization? I've been using it to parse JSON
http://developer.apple.com/library/ios/#documentation/Foundation/Reference/NSJSONSerialization_Class/Reference/Reference.html