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.
Related
I am using the following CoreFoundation function CFPropertyListCreateDeepCopy:
for converting the immutable objects to mutable objects.If any of the object is NULL the CFPropertyListCreateDeepCopy returning empty .Is there any work around for this.
self.packageArray = CFBridgingRelease(CFPropertyListCreateDeepCopy(NULL, (CFPropertyListRef)self.packageArray , kCFPropertyListMutableContainersAndLeaves));
CFPropertyListCreateDeepCopy fails to process array / dictionary containing NSNull
sample code
NSArray *immutable = #[ #"a", [NSNull null], #"c" ];
NSMutableArray *mutable = (__bridge
id)CFPropertyListCreateDeepCopy(kCFAllocatorDefault, (__bridge
CFArrayRef)immutable, kCFPropertyListMutableContainers);
sample json response from this link
Thanks in advance.
After few hours of workaround, I have solved this issue by below way.
Just place below line when converting API response to JSON Object.
responseString=[responseString stringByReplacingOccurrencesOfString:#"\":null" withString:#"\":\"\""];//To Handle Null Characters
//Search for below line in your parsing library and paste above code
data = [responseString dataUsingEncoding:NSUTF8StringEncoding];
So there will be no null characters in your JSON object, hence no issue with using CFPropertyListCreateDeepCopy.
Cheers!!
I am dynamically populating a NSMutableDictionary with keys that are identical. However doing so replaces the original keys value. What I require is it to be appended and not replacing the existing key. For example, I need a structure like
{
#"Key" : #"Value1",
#"Key" : #"Value2",
#"Key" : #"Value3"
}
I know I could add each NSDictionary that is created to a NSMutableArray but my issue comes because I need the input value to be a NSDictionary.
Currently I have the following which replaces the original value
NSMutableDictionary *ripDictionary = [[NSMutableDictionary alloc] init];
for(NSString *ripId in recievedRips){
//SOME OTHER CODE
ripDictionary[#"rip"] = keysAndAttributes;
[data addObject:ripDictionary];
}
From the NSDictionary Reference
A key-value pair within a dictionary is called an entry. Each entry consists of one object that represents the key and a second object that is that key’s value. Within a dictionary, the keys are unique. That is, no two keys in a single dictionary are equal (as determined by isEqual:).
Perhaps you could modify your code to accept a dictionary like:
{
#"Key" : [
#"Value1",
#"Value2",
#"Value3"
]
}
You can only have a single unique key per dictionary, so if you want multiple values associated with it then you would add those values to an array associated with the key.
if([aDictionary objectForKey:#"key"] != nil){
aDictionary[#"key"] = #[aDictionary[#"key"], bDictionary[#"key"]];
}else{
aDictionary[#"key"] = bDictionary[#"key"];
//OR make all aDictionary values array by default with a single value
//but you get the point
}
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
{
"success":true,
"listings":
{
"50831582253b4acf09000000":
{
"id":"50831582253b4acf09000000",
"title":"fddfds",
"assets":[],
"discussions":[]
}
},
"displaymessage":"1 Listings Found"
}
I am still struggling between dictionaries and arrays. What would make the above an Array?
Thanks
There's a pretty big difference between dictionaries and arrays. Dictionaries store data entries in relation to a keys you specify on instantiation. For example:
NSDictionary *myDictionary = [[NSDictionary alloc] initWithObjectsAndKeys:#"object1",#"key1",#"object2",#"key2", nil];
This alloc/inits a dictionary and sets "object1" for "key1" and "object2" for "key2", so then if you wanted to ask for the value of "key1" you could access it with the following.
NSLog(#"%#",[myDictionary objectForKey:#"key1"]);
Objects in a dictionary can be arrays, dictionaries, booleans, data, dates, numbers and strings.
On the other hand, arrays store data by the datas index within the array:
NSArray *myArray = [[NSArray alloc] initWithObjects:#"object1",#"object2",#"object3", nil];
You can then access the a specific bit of data from within the array by asking for objectAtIndex:, ex:
NSLog(#"%#",[myArray objectAtIndex:1]);
Which will return "object2" because the first index in an array is always "0".
Check the JSON docs
JSON Arrays are represented by square brackets
[ "object1", "object2" ]
JSON arrays are normally represented in Objective-C by NSArray. This matches up with the new literal syntax
#[ #"object1", #"object2" ]
JSON Objects are represented by curly brackets
{ "key" : "value" }
JSON objects are normally represented in Objective-C by NSDictionary. This also matches up with the new literal syntax
#{ #"key" : #"value" }
As you can probably tell, the entities or "cells" in the list are separated by commas (,)
In a serialized dictionary, an entity is broken into a key:value pair by a colon (:)
"success":true
Where the first part in quotes before the colon is the key, and the second part is the value that corresponds to that key.
A serialized array might look something like this:
{"hello", "goodbye", "world", "words", "friendship"}
Notice that entities are still separated by commas, but there are no colons outside of quotes.
Another key difference is that in dictionaries, keys must be unique, so you shouldn't have something like this:
{"success":true, ..., "success":false}
whereas in an array, elements do not have to be unique:
{"hello", "hello", "goodbye"}
Hope this helps =)
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