I am deserializing some JSON using the TouchJSON framework. The array of dictionaries that comes from the parsing is used to populate a UITableView.
There is a chance that some of the values in the JSON I parse are empty. This results, if I NSLog it to the console, in the values looking like this:
id = 1234;
title = "Hello, World";
description = "<null>";
detail = "The world says hello";
Here the description value was an empty string when retrieved from the server.
So TouchJSON recognizes that the description values is of type string, but the original intention of the server was to communicate that this was an empty string, like description = #"";
If I later on try to set the value of description, to a UILabels text property the app will crash.
So my questions are, I have both NSNumbers and NSStrings in the JSON, should I traverse the result from TouchJSON's deserialize method and test all values and how would I do so?
I can't simulated what would happen if an NSNumber value was empty, how would I test for this? Will the NSNumber value be nil in that case instead of "null"?
I was using the SBJSON library and came up against the same problem. My solution would apply to your case too: I changed the library so that it handled missing values, setting them to +[NSNull null] in the collection it returned. That makes your client code a little warty, because you have to handle the cases where you might get an NSNull instead of an NSString. But this is just a more obvious version of the wart where you have to decide whether #"" meant an empty string or an unset value.
Related
Using FMDB, bridged for use in Swift, I retrieved long integer values for a SQLite column definition like this
myColumn BigInt NULL UNIQUE
with a line of Swift code for an FMResultSet (based on a straightforward select query left out here) like this
let value = resultSet.longForColumnName("myColumn")
This worked fine. Yet, when I retrieved and then updated multiple records involving this column, I ran into a Unique Key Index violation. As it turned out, for NULL values, the above line of Swift code returned a value of 0, and I couldn't see a quick way to detect NULL values properly.
When searching for a proper way to handle this, the only related question I could find is this one concerning empty strings being returned for text columns with Null values. The answer didn't apply here. So, I'm adding the results of my research here, should they be useful to somebody else.
(The underlying problem turns out to not be specific to having the Unique constraint.)
The FMDB API, when bridged from the Objective-C version, seemingly has no direct way to check for NULL values (correct me, if I'm wrong, please). So, for a database column defined as, for example,
myColumn BigInt NULL
a NULL value will appear as value 0 in any FMResultSet involving this column with Swift code as shown in the question.
(This will have particularly surprising results when there happens to be a UNIQUE constraint on top. The NULL value will be retrieved as 0 from the database to be potentially updated as such with the next save operation, violating the Unique constraint when multiple entities are involved, as in my case. However, the underlyling problem is independent of the constraint. So, I'll focus on the problem of NULL values in general.)
To avoid this problem, we have to retrieve the column's value as an object from a respective FMResultSet first like so:
let objectValue = resultSet.objectForColumn("myColumn")
If objectValue happens to be of type/value NSNull(), then we have a NULL value and can handle it accordingly. Otherwise, we can then use the normal longForColumnName method. (For object types such as Strings, however, the FMDB implementation naturally returns an optional, which will be nil for database values of NULL!)
To make this easier, I use an extension of the FMResultSet class (for retrieval by index, which I prefer) like so:
extension FMResultSet {
func isNullForColumnIndex(columnIdx: Int32) -> Bool {
let value = self.objectForColumnIndex(columnIdx)
if let nullValue = value as? NSNull {
return true
} else {
return (value == nil)
}
}
}
This reduces the value extraction for a number type like in the example above to a line like this, assuming "myColumn" would appear at index 0 in the result set:
let num: Int64? = (result.isNullForColumnIndex(0) ? nil : Int64(result.longForColumnIndex(0)))
Surely, I could as well have added a method such as, for example, optionalLongForColumnIndex(columnIndex: Int32) -> Int64? that would include both the NULL-check and value retrieval. That would just require one such method for every value-type, which I have avoided so far.
Marco, your own answer is excellent: it describes exactly what is happening. Yet there are type-safer alternatives to ccgus/fmdb when coding in Swift. You could try https://github.com/stephencelis/SQLite.swift (pretty popular) or https://github.com/groue/GRDB.swift (closer to fmdb in the spirit).
I had a similar problem in Objective-C with FMDB.
Also getting 0 instead of nil when using longForColumnIndex.
However using:
NSNumber* object = [resultSet objectForColumnIndex:0];
worked as expected.
Gives a perfectly valid NSNumber object if a Value is stored, NSNull otherwise.
So this is the code I have:
[dataCenter.tempPalette replaceObjectAtIndex:9 withObject:selectedColour];
Object 9 does exist, and it's currently an int (not sure if that matters). selectedColour is also an int. dataCenter.tempPalette is a NSMutableArray.
The error it gives me is this:
Passing argument 2 of 'replaceObjectsAtIndex:withObject:' makes pointer from integer without a cast.
Any ideas?
ints aren't objects. If you want to store integers in an NSMutableArray, you'll need to turn them into NSNumbers first using +[NSNumber numberWithInt:]. The error message is complaining that you're passing an int where a pointer (to an object) is required.
The second param of replaceObjectAtIndex needs to be a valid pointer to an object. From your description, it sounds like you are trying to pass in an int value ( selectedColour ), which is not a valid reference to an object.
I believe, instead of passing in the selectedColour int value, you want to pass a reference to the actual color object. This would probably be a reference to a UIColor object.
Also keep in mind, if you are trying to store many objects representing the same color. For purposes of using memory efficiently, you will probably want to store in your tempPallette array references pointing to the same color object for those entries that use the same color.
Hope this helps. Good Luck.
is posible using JSON-Framework for Iphone to know if a tag exists inside the JSON like in JAVA with the function hasTag(String)?
If by tag you mean name/key and you’re using SBJSON, use -objectForKey: and test if the return value is nil. For instance, if person is an NSDictionary instance returned by the JSON parser and it can optionally contain a nickname,
if ([person objectForKey:#"nickname"] != nil)
{
// `nickname' is available; do something with it
}
The way I think to do it is to convert your JSON Object to an NSDictionary and after that use the method -(NSArray *)allKeys, or -(NSArray *)allValues depending what you want.
It returns an array with all the keys (or values) inside the object. You can then compare the keys with the one you want to find.
Hope it helps you.
Given this key/value pair:
FirstName = "Steve"
I'd like to set the property of an ABPerson in the iPhone Address Book, like so:
ABRecordSetValue(person, kABPersonFirstNameProperty, #"Steve", nil);
The problem is, I won't always know what key is submitted. (It will be equivalent to a property constant without the "kABPerson" before it, and "Property" after it).
The submitted key could be any number of properties: "JobTitle", "Note", etc.
How do I translate that key into the Address Book Property Constant. ie:
FirstName to kABPersonFirstNameProperty.
I was thinking I could use Key Value Coding, but I'm at a loss as to how I'd implement it in this case.
For the sake of coding efficiency, I'm trying to avoid using a long Switch statement, or using an array filled with every possible Property.
Update
I'm discovering here that I need to access a constant by it's string representation. But I don't know if that's possible. So kABPersonJobTitleProperty is a constant that equals 18. So I would love if the following code worked:
NSString *fieldName = [NSString stringWithString:#"JobTitle"];
NSString *propertyName = [NSString stringWithFormat:#"kABPerson%#Property", fieldName];
ABRecordRef person = ABPersonCreate();
ABRecordSetValue(person, propertyName, #"Fisherman", nil);
Of course, running this returns this warning passing argument 2 of 'ABRecordSetValue' makes integer from pointer without a cast. Because the string propertyName is not resolving to the integer or constant it's looking for.
Can anyone see a way to make this work?
My setup is similar to the question listed here:
Key-Value Coding
I'm getting xml data that I have control over, and turning it into properties of an Address Book entry.
This is more of a application design question than anything else. The way I see it with what you have, you're going to need a "long switch statement" somewhere. Maybe the cleanest way would be in a message you send yourself with a signature like:
- (ABPropertyID)recordPropertyFromMyEnum:(MyEnumType)property
Consider this: if the only enumerations you have line up one-for-one with an ABPropertyID, why not just use ABPropertyID instead?
I'm working in the IPhone SDK, and am pretty new to objective-c. Right now I'm working with NSUserDefaults to save and restore setting on my IPhone app. In order to save the classes that have been created, I encode them into dictionary form, and then save the NSdictionary.
My problem is that I can't find a reasonable way to store a non-value (I.E. a class variable that is nil) in the dictionary in a reasonable way. To be more specific, lets say I have a class "Dog" and it's got NSString *tail-color. Lets say I'm trying to save a class instance of a dog without a tail, so tail-color for that instance is nil. What is a reasonable way of saving dog as a dictionary? It won't let me save nil into the NSdictionary. #"" isn't good, because if I do if(#""), #"" is true. I would like it to be false like nil.
I hope my question makes sense, and thanks for your help!
If you don't store anything for that key, nil will be returned when you call objectForKey:. If you check for nil when reading the data in, would that be enough? Optionally, you can use objectsForKeys:notFoundMarker: that will return a default value instead of nil.
So, store nothing at all in the dictionary for a value you don't have, and use a strategy for handling that value missing when reading.
You could use NSNull, but that doesn't feel standard, IMO.
You can use NSNull. Instantiate it like this:
[NSNull null]
I would recommend Archiving to save and restore your objects, however.
You should use NSNull to represent nil objects in collections
The best solution is to not save the values which are 'nil' in your case. While reading if the value is not present for your given key the dictionary will return you 'nil'.