I'm storing the result of JSONKit parse in a key/value database (LevelDB), but the JSON I'm downloading has some filed set to null, and this won't let you serialize the corresponding generated object (NSArray or NSDictionary), to store it as an object.
¿Any idea how can I deep iterate over a NSSomething (Dictionary or Array) to change those values?
There is a post https://github.com/johnezang/JSONKit/issues/25 that explains how to modify the framework to omit it from dictionaries and array's
Sometime it happened to me that a null value was set to something what was not recognized neither as NSNull nor as NSString. Therefore i´ve replaced all null strings in the json string before i parse it with NSJSONSerialization. I´ve read the data into a NSData object, copied it into a NSString object, replaced the null strings and copied it again into a NSData object as NSJSONSerialization expects a NSData object. Maybe you can write it shorter but it works.
Here´s the code
NSString *jsonPath = [myPath stringByAppendingPathComponent:#"myDataFile.json"];
NSMutableData *myJSON = [[NSMutableData alloc] initWithContentsOfFile:jsonPath];
NSString *jsonString = [[NSString alloc] initWithBytes:myJSON.bytes length:myJSON.length encoding:NSUTF8StringEncoding];
jsonString = [jsonString stringByReplacingOccurrencesOfString:#"null" withString:#"\"placeholder\""];
NSData * jsonData = [jsonString dataUsingEncoding:NSUTF8StringEncoding];
NSMutableDictionary *json = [NSJSONSerialization JSONObjectWithData:jsonData options:NSJSONReadingMutableContainers error:&jsonParsingError];
after that all previous null occurances will contain a placeholder string.
I parsed the JSON to a mutableObject [[JSONDecoder decoder] mutableObjectWithUTF8String:(const unsigned char *) [json UTF8String] length:[json lengthOfBytesUsingEncoding:NSUTF8StringEncoding] error:nil ]; and then used this code to fix it:
-(id) fixObject: (id) a
{
if([a isKindOfClass:[NSMutableDictionary class]])
{
NSMutableDictionary *ad = a;
for (NSObject *key in ad)
{
if([[ad objectForKey:key] isKindOfClass:[NSMutableDictionary class]] || [[ad objectForKey:key] isKindOfClass:[NSMutableArray class]])
[self fixObject:[ad objectForKey:key]];
else
{
if((NSNull *)[ad objectForKey:key] == [NSNull null]) [ad setObject:#"" forKey:key];
}
}
}else if([a isKindOfClass:[NSMutableArray class]])
{
NSMutableArray *ar = a;
for (NSObject *ob in ar)
{
if([ob isKindOfClass:[NSMutableDictionary class]] || [ob isKindOfClass:[NSMutableArray class]])
{
[self fixObject:ob];
}
else if((NSNull *)ob == [NSNull null])
{
[ar removeObject:ob];
}
}
}
return a;
}
If you find a better way to do this, let me know!
Related
I get this JSON object from my web service
0: {
Username: "John"
Password: "12345"
Position: "Admin"
Job: "Developer"
ResourceJobId: 1
}
When I try to assign a value, for example, to Username JSON string, I get a semantic error:
id obj = [NSJSONSerialization JSONObjectWithData:data
options:NSJSONReadingAllowFragments
error:&error];
for (NSDictionary *dictionary in array) {
NSString *usernameString = #"";
//this line of code gives me an error: Expression is not assignable
[dictionary objectForKey:#"Username"] = usernameString ;
I know how get the JSON object with NSJSONSerialization class, but my target is how assign the value #"" to the JSON object.
So how can I assign the value #"" to the Username JSON string?
While you are trying to fetched dictionary from array. it is not mutable dictionary so you need to created NSMutableDictionary while fetching data from Array. following code will help you to change username.
for (NSMutableDictionary *dictionary in array)
{
NSMutableDictionary* dictTemp = [NSMutableDictionary dictionaryWithDictionary:dictionary];
[dictTemp setObject:usernameString forKey#"Username"];
[_arrayList replaceObjectAtIndex:index withObject:dictTemp];
}
Well your assignment operation is written wrong, it should be indeed like this:
If you need to get value from dictionary
usernameString = [dictionary objectForKey:#"Username"];
If you want to set a value to your dictionary, first of all your dictionary should be NSMutableDictionary
[dictionary setObject:usernameString forKey:#"Username"];
NSData *data = [NSData dataWithContentsOfURL:url];
NSError *error;
id obj = [NSJSONSerialization JSONObjectWithData:data
options:NSJSONReadingAllowFragments
error:&error];
if(!error && [obj isKindOfClass:[NSArray class]]){
NSArray *array = (NSArray *)obj;
Booking *booking = nil;
for (NSMutableDictionary *dictionary in array) {
NSString *usernameString = #"";
//this line of code gives me an error: Expression is not assignable
[dictionary objectForKey:#"Username"] = usernameString;
Yes, is a compiler error
You need to have a mutabledictionary inorder to change the value. Try this
for (NSDictionary *dictionary in array) {
NSMutableDictionary *mutableDictionary = [NSMutableDictionary dictionaryWithDictionary:dictionary];
NSString *usernameString = #"";
[mutableDictionary setValue:#"" forKey:usernameString];
}
Even the answers are correct, I want to add that you do not have to do this your own. NSJSONSerialization already has a solution for that. Simply pass as options one of these:
NSJSONReadingMutableContainers = (1UL << 0),
NSJSONReadingMutableLeaves = (1UL << 1),
when reading the JSON.
When check self.weatherData, I get nothing even though there is data in "data". Here is my function:
- (void)handleNetworkResponse:(NSData *)myData
{
//NSMutableDictionary *data = [NSMutableDictionary dictionary];
NSMutableDictionary *data = [[NSMutableDictionary alloc] init];
// now we'll parse our data using NSJSONSerialization
id myJSON = [NSJSONSerialization JSONObjectWithData:myData options:NSJSONReadingMutableContainers error:nil];
// typecast an array and list its contents
NSDictionary *jsonArray = (NSDictionary *)myJSON;
// take a look at all elements in the array
for (id element in jsonArray) {
id key = [element description];
id innerArr = [jsonArray objectForKey:key];
NSDictionary *inner = (NSDictionary *)innerArr;
if ([inner conformsToProtocol:#protocol(NSFastEnumeration)]) {
for(id ele in inner) {
id innerKey = [ele description];
[data setObject:[[inner valueForKey:innerKey] description] forKey:[ele description]];
}
}
else {
[data setObject:[inner description] forKey:[element description]];
}
}
NSLog([data description]);
self.weatherData = data;
}
However when check self.weatherData, I get nothing even though there is data in "data".
Issue was data isn't there when I assign it to the variable from the an asynchronous method :D
all fixed now by adding a delegate call back
I have a NSDictionary and what to get the datatype for value given the key
is it possible?
You could check the variants you accept by
id objectValue = [dictionary valueForKey:#"SomeKey"];
if ([objectValue isKindOfClass:[NSString class]]) {
//Object is a NSString
} else if ([objectValue isKindOfClass:[NSArray class]]) {
//Object is a NSArray
} else if ([objectValue isKindOfClass:[NSDictionary class]]) {
//Object is a NSDictionary
} else if ([objectValue isKindOfClass:[NSNumber class]]) {
//Object is a NSNumber
}
And so on.. In this pattern just handle all the types your app supports. Ignore values your app doesn't support by this pattern or just fail gracefully in another way when you don't support the datatype of the value
To just figure out what class it is (to debug the application for example) you can do:
NSString *className = NSStringFromClass([objectValue class]);
you could use NSStringFromClass to get the type, or failing that you could use isKindOfClass:
NSDictionary *dictionary = #{
#"string": #"Something",
#"number": #(1),
#"null": [NSNull null],
#"custom": [[CustomType alloc] init]
};
[dictionary enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) {
NSLog(#"type = %#", NSStringFromClass([obj class]));
}];
output:
type = CustomType
type = NSNull
type = __NSCFNumber
type = __NSCFConstantString
I have in a NSString "[{"van" : 1,312, "vuan":12,123}] and in order to get this values for every key, I am doing this:
NSData *data1 = [jsonResponse1 dataUsingEncoding:NSUTF8StringEncoding];
jsonArray = [NSJSONSerialization JSONObjectWithData:data1 options:kNilOptions error:&err];
self.van = [NSMutableArray arrayWithCapacity:1];
self.vuan = [NSMutableArray arrayWithCapacity:1];
for (NSDictionary *json in jsonArray) {
NSString * value = [json objectForKey:#"van"];
[self.van addObject:value];
lbl1.text = value;
NSString * value1 = [json objectForKey:#"vuan"];
[self.vuan addObject:value1];
lbl4.text = value1;
}
May be I don't have to use an array and instead to convert the NSData directly in a NSDictionary.
Anyway, I don't understand why jsonArray is nil, although jsonResponse1 contains the values I have written above.
EDIT: My boss have written the json string wrong. Thank you all for your suggestions!:)
Your JSON is invalid. Fix it. This site is your friend.
http://jsonlint.com/
You need to code more defensively and you need to report errors as they are found.
Firstly check if the JSON parsing failed and if so report the error:
NSData *data1 = [jsonResponse1 dataUsingEncoding:NSUTF8StringEncoding];
jsonArray = [NSJSONSerialization JSONObjectWithData:data1 options:kNilOptions error:&err];
if (jsonArray == nil)
{
NSLog(#"Failed to parse JSON: %#", [err localizedDescription]);
return;
}
Secondly if those keys are not in the JSON, objectForKey: will return nil and when you attempt to add that to the arrays, it will throw an exception, which is something you want to avoid:
for (NSDictionary *json in jsonArray) {
NSString * value = [json objectForKey:#"van"];
if (value != nil)
{
[self.van addObject:value];
lbl1.text = value;
}
else
{
NSLog(#"No 'van' key in JSON");
}
NSString * value1 = [json objectForKey:#"vuan"];
if (value1 != nil)
{
[self.vuan addObject:value1];
lbl4.text = value1;
}
else
{
NSLog(#"No 'vuan' key in JSON");
}
}
So in summary: runtime errors will occur so you need to ensure you handle them. When they occur you need to report them with as much information possible so that you can diagnose and fix them.
I have some code that looks like this:
NSDictionary* json = [NSJSONSerialization JSONObjectWithData:data
options:kNilOptions
error:&error];
NSArray *arrRequests = [NSJSONSerialization JSONObjectWithData:data
options:NSJSONReadingMutableContainers
error:nil];
// Loop through the array and generate the necessary annotation views
for (int i = 0; i<= arrRequests.count - 1; i++)
{
//now let's dig out each and every json object
NSDictionary *dict = [arrRequests objectAtIndex:i];
NSString *is_private = [NSString
stringWithString:[dict objectForKey:#"is_private"]];
...
It works when the value for is_private is 1 or 0, but if it is null it crashes with an exception on this line:
NSString *is_private = [NSString stringWithString:[dict objectForKey:#"is_private"]];
Is there a way to check if it is not null or handle this so that it is able to put nil into the NSString *is_private variable?
Thanks!
You should be able to handle it like so:
id value = [dict valueForKey:#"is_private"];
NSString *is_private = [value isEqual:[NSNull null]] ? nil : value;
Your problem has nothing to do with NSJSONSerialization and everything to do with the fact that you're passing a nil value to stringWithString:. Why not just simply that line to NSString *is_private = [dict objectForKey:#"is_private"];?
Also, why are you using an NSString to store a boolean value? An NSNumber would be much better-suited.
Why don't you just check if [dict objectForKey:#"is_private"] is nil or [NSNull null] before passing it to stringWithString:?