creating a prefix NSString using two NSStrings - ios

I have instructions to make a prefix method that takes two strings for each position where mask = 0 and the first string = second string up until these conditions are not meet that is your prefix NSString.
I made my attempt but for some reason my prefix string is returning as null and I was hoping i could get some help.
here is my method
- (void)prefixCalculation:(NSString *)seriesStart SeriesEnd:(NSString *)seriesEnd {
// call this method when loading the view to get everything set up
NSLog(#"start %#", seriesStart);
NSLog(#"end %#", seriesEnd);
// allocate values so you can use this to create the UITextField
seriesStartString = seriesStart;
seriesEndString = seriesEnd;
// set prefix string
for (int i = 0; i <= seriesStartString.length ; i++) {
unichar c1 = [seriesStartString characterAtIndex:i];
unichar c2 = [seriesEndString characterAtIndex:i];
if (c1 != c2) {
break;
}
else if (c1 == c2) {
NSString *str = [NSString stringWithFormat: #"%C", c1];
[prefixString appendFormat:#"%#",str];
}
}
NSLog(#"prefix %#", prefixString);
}
I am not sure what I am doing wrong but prefixString which is a NSMutableStrong comes back as null, any help would be greatly appreciated.

Since in your code you don't show the initialization of prefixString, I take a guess and suggest you to check whether you initialized it or not.
If that's not the case, prefixString is nil and sending messages to it will fail silently.

Related

Objective C Message Argument Array Parsing (AJNMessageArgument)

I'm working with AllJoyn on iOS using objective C. I'm having trouble parsing an ALLJOYN_ARRAY type in objective C. The problem is that the MsgArg type (C++) is abstracted through the AJNMessagArgument type (objective c). The sample code for parsing an array signature of "a{iv}" in c++ is as follows:
MsgArg *entries;
size_t num;
arg.Get("a{iv}", &num, &entries);
for (size_t i = 0; i > num; ++i) {
char *str1;
char *str2;
uint32_t key;
status = entries[i].Get("{is}", &key, &str1);
if (status == ER_BUS_SIGNATURE_MISMATCH) {
status = entries[i].Get("{i(ss)}", &key, &str1, &str2);
}
}
Now in objective c, the msgarg is the handle of the AJNMessageArgument type. I've tried the following to try getting this to work with no avail:
AJNMessageArgument *strings = [AJNMessageArgument new];
size_t numVals;
QStatus status = [supportedLangsArg value: #"as", &numVals, strings.handle];
if(status != ER_OK){
NSLog(#"ERROR: Could not supported languages from the message argument");
}
This returns ER_OK, but I can't see any data in the handle via the debugger like I can with valid AJNMessageArguments.
Passing in &strings.handle throws a compile error "Address of property expression required".
I've tried quite a few other things, but none make much sense compared to the one above.
Please help me! I need an example of how to parse an "as" signature in objc. I haven't been able to find any docs for this.
Thanks for any help!
Ok, short story is this can't be done without adding custom code to the AJNMessageArgument Class. This is because in this scenario, the "value" method will return a pointer to an array of MsgArg types. Objective C cannot interact with MsgArg - Which is the whole reason they created the AJNMessageArgument wrapper for Objective C.
Here is how it is done:
Add this static method to your AJNMessageArgument.mm class:
+ (NSArray*)getAJNMessageArgumentArrayFromMsgArgArray:(void*)arg : (int)size
{
NSMutableArray * toReturn = [NSMutableArray new];
MsgArg *msgArray = (MsgArg*) arg;
for (int i = 0; i < size; ++i)
{
void * msarg = malloc(sizeof(MsgArg));
MsgArg arg = msgArray[i];
memcpy(msarg, &msgArray[i], sizeof(MsgArg));
AJNMessageArgument *toAdd = [[AJNMessageArgument alloc] initWithHandle:msarg];
[toReturn addObject:toAdd];
}
return [toReturn copy];
}
Don't forget to add the method definition to the AJNMessageArgument.h file:
+ (NSMutableArray*)getAJNMessageArgumentArrayFromMsgArgArray:(void*)arg : (int)size
So now, in our objective C code, we can parse the AJNMessageArgument with signature "as" - but we can't cast it to the MsgArg type yet because we can't access that structure outside of objc++ - so we will use a (void *).
+ (NSArray*)getSupportedLangsFromMessageArgument:(AJNMessageArgument*)supportedLangsArg
{
void *strings; //void * to keep track of MsgArg array data.
size_t numVals;
QStatus status = [supportedLangsArg value: #"as", &numVals, &strings];
if(status != ER_OK){
NSLog(#"ERROR: Could not supported languages from the message argument");
}
NSMutableArray *arrayOfMsgArgs = [AJNMessageArgument getAJNMessageArgumentArrayFromMsgArgArray:strings :numVals];
//Now loop through the resulting AJNMessageArguments of type ALLJOYN_STRING - and parse out the string.
NSMutableArray *arrayOfStrings = [NSMutableArray new];
for (AJNMessageArgument *arg in arrayOfMsgArgs) {
NSString* msgArgValue = [AboutUtil getStringFromMessageArgument:arg];
[arrayOfStrings addObject:msgArgValue];
}
return [arrayOfStrings copy];
}
Now we have an NSArray of NSStrings. Whew.
In case you were wanting to see the code to get the NSString out of the AJNMessageArguments that are in the array, here is that method:
+ (NSString*)getStringFromMessageArgument:(AJNMessageArgument*)msgarg
{
char *charStr;
QStatus status = [msgarg value:#"s", &charStr];
if (status != ER_OK) {
NSLog(#"Error");
}
NSString *str = [NSString stringWithFormat:#"%s", charStr];
return str;
}
Happy AllJoyn-ing.

Large NSNumber to NSString conversion [duplicate]

Consider this code:
NSNumber* interchangeId = dict[#"interchangeMarkerLogId"];
long long llValue = [interchangeId longLongValue];
double dValue = [interchangeId doubleValue];
NSNumber* doubleId = [NSNumber numberWithDouble:dValue];
long long llDouble = [doubleId longLongValue];
if (llValue > 1000000) {
NSLog(#"Have Marker iD = %#, interchangeId = %#, long long value = %lld, doubleNumber = %#, doubleAsLL = %lld, CType = %s, longlong = %s", self.iD, interchangeId, llValue, doubleId, llDouble, [interchangeId objCType], #encode(long long));
}
The results:
Have Marker iD = (null), interchangeId = 635168520811866143,
long long value = 635168520811866143, doubleNumber = 6.351685208118661e+17,
doubleAsLL = 635168520811866112, CType = d, longlong = q
dict is coming from NSJSONSerialization, and the original JSON source data is "interchangeId":635168520811866143. It appears that all 18 digits of the value have been captured in the NSNumber, so it could not possibly have been accumulated by NSJSONSerialization as a double (which is limited to 16 decimal digits). Yet, objCType is reporting that it's a double.
We find this in the documentation for NSNumber: "The returned type does not necessarily match the method the receiver was created with." So apparently this is a "feechure" (i.e., documented bug).
So how can I determine that this value originated as an integer and not a floating point value, so I can extract it correctly, with all the available precision? (Keep in mind that I have some other values that are legitimately floating-point, and I need to extract those accurately as well.)
I've come up with two solutions so far:
The first, which does not make use of knowledge of NSDecimalNumber --
NSString* numberString = [obj stringValue];
BOOL fixed = YES;
for (int i = 0; i < numberString.length; i++) {
unichar theChar = [numberString characterAtIndex:i];
if (theChar != '-' && (theChar < '0' || theChar > '9')) {
fixed = NO;
break;
}
}
The second, which assumes that we only need worry about NSDecimalNumber objects, and can trust the CType results from regular NSNumbers --
if ([obj isKindOfClass:[NSDecimalNumber class]]) {
// Need to determine if integer or floating-point. NSDecimalNumber is a subclass of NSNumber, but it always reports it's type as double.
NSDecimal decimalStruct = [obj decimalValue];
// The decimal value is usually "compact", so may have a positive exponent even if integer (due to trailing zeros). "Length" is expressed in terms of 4-digit halfwords.
if (decimalStruct._exponent >= 0 && decimalStruct._exponent + 4 * decimalStruct._length < 20) {
sqlite3_bind_int64(pStmt, idx, [obj longLongValue]);
}
else {
sqlite3_bind_double(pStmt, idx, [obj doubleValue]);
}
}
else ... handle regular NSNumber by testing CType.
The second should be more efficient, especially since it does not need to create a new object, but is slightly worrisome in that it depends on "undocumented behavior/interface" of NSDecimal -- the meanings of the fields are not documented anywhere (that I can find) and are said to be "private".
Both appear to work.
Though on thinking about it a bit -- The second approach has some "boundary" problems, since one can't readily adjust the limits to assure that the maximum possible 64-bit binary int will "pass" without risking loss of a slightly larger number.
Rather unbelievably, this scheme fails in some cases:
BOOL fixed = NO;
long long llValue = [obj longLongValue];
NSNumber* testNumber = [[NSNumber alloc] initWithLongLong:llValue];
if ([testNumber isEqualToNumber:obj]) {
fixed = YES;
}
I didn't save the value, but there is one for which the NSNumber will essentially be unequal to itself -- the values both display the same but do not register as equal (and it is certain that the value originated as an integer).
This appears to work, so far:
BOOL fixed = NO;
if ([obj isKindOfClass:[NSNumber class]]) {
long long llValue = [obj longLongValue];
NSNumber* testNumber = [[[obj class] alloc] initWithLongLong:llValue];
if ([testNumber isEqualToNumber:obj]) {
fixed = YES;
}
}
Apparently isEqualToNumber does not work reliably between an NSNumber and an NSDecimalNumber.
(But the bounty is still open, for the best suggestion or improvement.)
As documented in NSDecimalNumber.h, NSDecimalNumber always returns "d" for it's return type. This is expected behavior.
- (const char *)objCType NS_RETURNS_INNER_POINTER;
// return 'd' for double
And also in the Developer Docs:
Returns a C string containing the Objective-C type of the data contained in the
receiver, which for an NSDecimalNumber object is always “d” (for double).
CFNumberGetValue is documented to return false if the conversion was lossy. In the event of a lossy conversion, or when you encounter an NSDecimalNumber, you will want to fall back to using the stringValue and then use sqlite3_bind_text to bind it (and use sqlite's column affinity).
Something like this:
NSNumber *number = ...
BOOL ok = NO;
if (![number isKindOfClass:[NSDecimalNumber class]]) {
CFNumberType numberType = CFNumberGetType(number);
if (numberType == kCFNumberFloat32Type ||
numberType == kCFNumberFloat64Type ||
numberType == kCFNumberCGFloatType)
{
double value;
ok = CFNumberGetValue(number, kCFNumberFloat64Type, &value);
if (ok) {
ok = (sqlite3_bind_double(pStmt, idx, value) == SQLITE_OK);
}
} else {
SInt64 value;
ok = CFNumberGetValue(number, kCFNumberSInt64Type, &value);
if (ok) {
ok = (sqlite3_bind_int64(pStmt, idx, value) == SQLITE_OK);
}
}
}
// We had an NSDecimalNumber, or the conversion via CFNumberGetValue() was lossy.
if (!ok) {
NSString *stringValue = [number stringValue];
ok = (sqlite3_bind_text(pStmt, idx, [stringValue UTF8String], -1, SQLITE_TRANSIENT) == SQLITE_OK);
}
Simple answer: You can't.
In order to do what you're asking, you'll need to keep track of the exact type on your own. NSNumber is more of a "dumb" wrapper in that it helps you use standard numbers in a more objective way (as Obj-C objects). Using solely NSNumber, -objCType is your only way. If you want another way, you'd have to do it on your own.
Here are some other discussions that may be of help:
get type of NSNumber
What's the largest value an NSNumber can store?
Why is longLongValue returning the incorrect value
NSJSONSerialization unboxes NSNumber?
NSJSONSerializer returns:
an integer NSNumber for integers up to 18 digits
an NSDecimalNumber for integers with 19 or more digits
a double NSNumber for numbers with decimals or exponent
a BOOL NSNumber for true and false.
Compare directly with the global variables kCFBooleanFalse and kCFBooleanTrue (spelling might be wrong) to find booleans. Check isKindOfClass:[NSDecimalNumber class] for decimal numbers; these are actually integers. Test
strcmp (number.objCType, #encode (double)) == 0
for double NSNumbers. This will unfortunately match NSDecimalNumber as well, so test that first.
Ok--It's not 100% ideal, but you add a little bit of code to SBJSON to achieve what you want.
1. First, add NSNumber+SBJson to the SBJSON project:
NSNumber+SBJson.h
#interface NSNumber (SBJson)
#property ( nonatomic ) BOOL isDouble ;
#end
NSNumber+SBJson.m
#import "NSNumber+SBJSON.h"
#import <objc/runtime.h>
#implementation NSNumber (SBJson)
static const char * kIsDoubleKey = "kIsDoubleKey" ;
-(void)setIsDouble:(BOOL)b
{
objc_setAssociatedObject( self, kIsDoubleKey, [ NSNumber numberWithBool:b ], OBJC_ASSOCIATION_RETAIN_NONATOMIC ) ;
}
-(BOOL)isDouble
{
return [ objc_getAssociatedObject( self, kIsDoubleKey ) boolValue ] ;
}
#end
2. Now, find the line in SBJson4StreamParser.m where sbjson4_token_real is handled. Change the code as follows:
case sbjson4_token_real: {
NSNumber * number = #(strtod(token, NULL)) ;
number.isDouble = YES ;
[_delegate parserFoundNumber:number ];
[_state parser:self shouldTransitionTo:tok];
break;
}
note the bold line... this will mark a number created from a JSON real as a double.
3. Finally, you can check the isDouble property on your number objects decoded via SBJSON
HTH
edit:
(Of course you could generalize this and replace the added isDouble with a generic type indicator if you like)
if ([data isKindOfClass: [NSNumber class]]) {
NSNumber *num = (NSNumber *)data;
if (strcmp([data objCType], #encode(float)) == 0) {
return [NSString stringWithFormat:#"%0.1f} ",num.floatValue];
} else if (strcmp([data objCType], #encode(double)) == 0) {
return [NSString stringWithFormat:#"%0.1f} ",num.doubleValue];
} else if (strcmp([data objCType], #encode(int)) == 0) {
return [NSString stringWithFormat:#"%d} ",num.intValue];
} else if (strcmp([data objCType], #encode(BOOL)) == 0) {
return num.boolValue ? #"Yes} " : #"No} ";
} else if (strcmp([data objCType], #encode(long)) == 0) {
return [NSString stringWithFormat:#"%ld} ",num.longValue];
}
}

IOS String length comparison issue

I'm struggling with an if Comparison - I basically want to make two comparisons - both of which need to pass - Firstly a basic if a string variable is equal to 'rec' and secondly if a strings character limit is not equal to zero.
I've tried various combinations - but this is where i'm at at the mo..
ArticleObject *A = [self.articleArray objectAtIndex:indexPath.section];
NSInteger imglength = [A.arImage length];
if([A.arRec isEqual: #"rec"] ) && (imglength !=Nil){
return 195;
}
else return 50;
I get an expected identifier error on the (imglength comparison - as in this screen shot
Can anyone shed any light for me please?
There are several things you should change:
ArticleObject *A = self.articleArray[indexPath.section];
NSInteger imglength = [A.arImage length];
if (imglength && [A.arRec isEqualToString:#"rec"]) {
return 195;
} else {
return 50;
}
Don't use Nil (or nil) with primitive types.
Your parentheses are messed up:
if([A.arec isEqualToString:#"rec"] && (imglengyb !=Nil))
^--------------//here
Maybe a better way would be:
if([A.arec isEqualToString:#"rec"] && [[A.arImage length] != 0])

How do i perform an arabic search in SQL Lite ignoring diacritics?

I'm trying to extract words that are stored in my SQL Lite database with ignoring diacritics but it always return an empty result. my database contains arabic words with diacritics and i would to make a search with words which are don't contain diacritics.
NSString *queryStatement = [[NSString alloc ]initWithFormat:#"SELECT ID,ARABIC, ARABICMEANING, FRENCHINARABIC, FRENCH, BEGINARABIC,BEGINFRENCH,ISFAVORITE FROM DictionaryDB WHERE FRENCHINARABIC LIKE \"%%%#%%\"",searchedWord];
For example searchedWord can be #"أكل" and can be with diacritics #"أَكَلَ".
How can i resolve this problem?
I solved such a thing by creating my own SQLite function that does this.
The basic idea is your query becomes:
NSString *queryStatement = [[NSString alloc] initWithFormat:#"SELECT ID, ARABIC, ARABICMEANING, FRENCHINARABIC, FRENCH, BEGINARABIC, BEGINFRENCH, ISFAVORITE FROM DictionaryDB WHERE contains(FRENCHINARABIC, '%#')", searchedWord];
where contains will be your custom function.
First you need to write a C function that implements the contains SQL function:
void contains(sqlite3_context *context, int argc, sqlite3_value **argv) {
BOOL res = NO;
if (argc < 2) {
res = NO;
} else {
char *textstr = (char *)sqlite3_value_text(argv[0]);
char *substr = (char *)sqlite3_value_text(argv[1]);
if (textstr && substr) {
NSString *text = [NSString stringWithCString:textstr encoding:NSUTF8StringEncoding];
NSString *sub = [NSString stringWithCString:substr encoding:NSUTF8StringEncoding];
// Adjust the options to suit your needs
NSRange range = [text rangeOfString:sub options:NSCaseInsensitiveSearch | NSDiacriticInsensitiveSearch | NSWidthInsensitiveSearch];
if (range.location != NSNotFound) {
res = YES;
}
}
}
sqlite3_result_int(context, res ? 1 : 0);
}
When you open your database connection you need to register this function:
// dbRef is your database reference
int res = sqlite3_create_function(dbRef, "contains", 2, SQLITE_UTF8, NULL, &contains, NULL, NULL);
if (res != SQLITE_OK) {
NSAssert1(0, #"Error: failed to create function in the database: '%s'.", sqlite3_errmsg(dbRef));
}
Side note - it's a bad idea to use stringWithFormat: to create your query. You should really consider using the sqlite3_bind_xxx functions to properly bind a value to a query. Using stringWithFormat: will fail if the value has any quotes or other special values. Using the sqlite3_bind_xxx functions takes care of properly quoting and escaping values.
I solved the problem by replacing the diacritics
here my correct query
#"SELECT ID,ARABIC, ARABICMEANING, FRENCHINARABIC, FRENCH, BEGINARABIC,BEGINFRENCH,ISFAVORITE FROM DictionaryDB WHERE
replace(replace(replace(replace(replace(replace(replace (replace(ARABICMEANING, 'ِ', ''),'ٍ',''),'ْ',''),'ّ','' ),'ٌ',''),'ُ',''),'ً',''),'َ','')LIKE \"%%%#%%\"",#"أكل"];

How to determine the true data type of an NSNumber?

Consider this code:
NSNumber* interchangeId = dict[#"interchangeMarkerLogId"];
long long llValue = [interchangeId longLongValue];
double dValue = [interchangeId doubleValue];
NSNumber* doubleId = [NSNumber numberWithDouble:dValue];
long long llDouble = [doubleId longLongValue];
if (llValue > 1000000) {
NSLog(#"Have Marker iD = %#, interchangeId = %#, long long value = %lld, doubleNumber = %#, doubleAsLL = %lld, CType = %s, longlong = %s", self.iD, interchangeId, llValue, doubleId, llDouble, [interchangeId objCType], #encode(long long));
}
The results:
Have Marker iD = (null), interchangeId = 635168520811866143,
long long value = 635168520811866143, doubleNumber = 6.351685208118661e+17,
doubleAsLL = 635168520811866112, CType = d, longlong = q
dict is coming from NSJSONSerialization, and the original JSON source data is "interchangeId":635168520811866143. It appears that all 18 digits of the value have been captured in the NSNumber, so it could not possibly have been accumulated by NSJSONSerialization as a double (which is limited to 16 decimal digits). Yet, objCType is reporting that it's a double.
We find this in the documentation for NSNumber: "The returned type does not necessarily match the method the receiver was created with." So apparently this is a "feechure" (i.e., documented bug).
So how can I determine that this value originated as an integer and not a floating point value, so I can extract it correctly, with all the available precision? (Keep in mind that I have some other values that are legitimately floating-point, and I need to extract those accurately as well.)
I've come up with two solutions so far:
The first, which does not make use of knowledge of NSDecimalNumber --
NSString* numberString = [obj stringValue];
BOOL fixed = YES;
for (int i = 0; i < numberString.length; i++) {
unichar theChar = [numberString characterAtIndex:i];
if (theChar != '-' && (theChar < '0' || theChar > '9')) {
fixed = NO;
break;
}
}
The second, which assumes that we only need worry about NSDecimalNumber objects, and can trust the CType results from regular NSNumbers --
if ([obj isKindOfClass:[NSDecimalNumber class]]) {
// Need to determine if integer or floating-point. NSDecimalNumber is a subclass of NSNumber, but it always reports it's type as double.
NSDecimal decimalStruct = [obj decimalValue];
// The decimal value is usually "compact", so may have a positive exponent even if integer (due to trailing zeros). "Length" is expressed in terms of 4-digit halfwords.
if (decimalStruct._exponent >= 0 && decimalStruct._exponent + 4 * decimalStruct._length < 20) {
sqlite3_bind_int64(pStmt, idx, [obj longLongValue]);
}
else {
sqlite3_bind_double(pStmt, idx, [obj doubleValue]);
}
}
else ... handle regular NSNumber by testing CType.
The second should be more efficient, especially since it does not need to create a new object, but is slightly worrisome in that it depends on "undocumented behavior/interface" of NSDecimal -- the meanings of the fields are not documented anywhere (that I can find) and are said to be "private".
Both appear to work.
Though on thinking about it a bit -- The second approach has some "boundary" problems, since one can't readily adjust the limits to assure that the maximum possible 64-bit binary int will "pass" without risking loss of a slightly larger number.
Rather unbelievably, this scheme fails in some cases:
BOOL fixed = NO;
long long llValue = [obj longLongValue];
NSNumber* testNumber = [[NSNumber alloc] initWithLongLong:llValue];
if ([testNumber isEqualToNumber:obj]) {
fixed = YES;
}
I didn't save the value, but there is one for which the NSNumber will essentially be unequal to itself -- the values both display the same but do not register as equal (and it is certain that the value originated as an integer).
This appears to work, so far:
BOOL fixed = NO;
if ([obj isKindOfClass:[NSNumber class]]) {
long long llValue = [obj longLongValue];
NSNumber* testNumber = [[[obj class] alloc] initWithLongLong:llValue];
if ([testNumber isEqualToNumber:obj]) {
fixed = YES;
}
}
Apparently isEqualToNumber does not work reliably between an NSNumber and an NSDecimalNumber.
(But the bounty is still open, for the best suggestion or improvement.)
As documented in NSDecimalNumber.h, NSDecimalNumber always returns "d" for it's return type. This is expected behavior.
- (const char *)objCType NS_RETURNS_INNER_POINTER;
// return 'd' for double
And also in the Developer Docs:
Returns a C string containing the Objective-C type of the data contained in the
receiver, which for an NSDecimalNumber object is always “d” (for double).
CFNumberGetValue is documented to return false if the conversion was lossy. In the event of a lossy conversion, or when you encounter an NSDecimalNumber, you will want to fall back to using the stringValue and then use sqlite3_bind_text to bind it (and use sqlite's column affinity).
Something like this:
NSNumber *number = ...
BOOL ok = NO;
if (![number isKindOfClass:[NSDecimalNumber class]]) {
CFNumberType numberType = CFNumberGetType(number);
if (numberType == kCFNumberFloat32Type ||
numberType == kCFNumberFloat64Type ||
numberType == kCFNumberCGFloatType)
{
double value;
ok = CFNumberGetValue(number, kCFNumberFloat64Type, &value);
if (ok) {
ok = (sqlite3_bind_double(pStmt, idx, value) == SQLITE_OK);
}
} else {
SInt64 value;
ok = CFNumberGetValue(number, kCFNumberSInt64Type, &value);
if (ok) {
ok = (sqlite3_bind_int64(pStmt, idx, value) == SQLITE_OK);
}
}
}
// We had an NSDecimalNumber, or the conversion via CFNumberGetValue() was lossy.
if (!ok) {
NSString *stringValue = [number stringValue];
ok = (sqlite3_bind_text(pStmt, idx, [stringValue UTF8String], -1, SQLITE_TRANSIENT) == SQLITE_OK);
}
Simple answer: You can't.
In order to do what you're asking, you'll need to keep track of the exact type on your own. NSNumber is more of a "dumb" wrapper in that it helps you use standard numbers in a more objective way (as Obj-C objects). Using solely NSNumber, -objCType is your only way. If you want another way, you'd have to do it on your own.
Here are some other discussions that may be of help:
get type of NSNumber
What's the largest value an NSNumber can store?
Why is longLongValue returning the incorrect value
NSJSONSerialization unboxes NSNumber?
NSJSONSerializer returns:
an integer NSNumber for integers up to 18 digits
an NSDecimalNumber for integers with 19 or more digits
a double NSNumber for numbers with decimals or exponent
a BOOL NSNumber for true and false.
Compare directly with the global variables kCFBooleanFalse and kCFBooleanTrue (spelling might be wrong) to find booleans. Check isKindOfClass:[NSDecimalNumber class] for decimal numbers; these are actually integers. Test
strcmp (number.objCType, #encode (double)) == 0
for double NSNumbers. This will unfortunately match NSDecimalNumber as well, so test that first.
Ok--It's not 100% ideal, but you add a little bit of code to SBJSON to achieve what you want.
1. First, add NSNumber+SBJson to the SBJSON project:
NSNumber+SBJson.h
#interface NSNumber (SBJson)
#property ( nonatomic ) BOOL isDouble ;
#end
NSNumber+SBJson.m
#import "NSNumber+SBJSON.h"
#import <objc/runtime.h>
#implementation NSNumber (SBJson)
static const char * kIsDoubleKey = "kIsDoubleKey" ;
-(void)setIsDouble:(BOOL)b
{
objc_setAssociatedObject( self, kIsDoubleKey, [ NSNumber numberWithBool:b ], OBJC_ASSOCIATION_RETAIN_NONATOMIC ) ;
}
-(BOOL)isDouble
{
return [ objc_getAssociatedObject( self, kIsDoubleKey ) boolValue ] ;
}
#end
2. Now, find the line in SBJson4StreamParser.m where sbjson4_token_real is handled. Change the code as follows:
case sbjson4_token_real: {
NSNumber * number = #(strtod(token, NULL)) ;
number.isDouble = YES ;
[_delegate parserFoundNumber:number ];
[_state parser:self shouldTransitionTo:tok];
break;
}
note the bold line... this will mark a number created from a JSON real as a double.
3. Finally, you can check the isDouble property on your number objects decoded via SBJSON
HTH
edit:
(Of course you could generalize this and replace the added isDouble with a generic type indicator if you like)
if ([data isKindOfClass: [NSNumber class]]) {
NSNumber *num = (NSNumber *)data;
if (strcmp([data objCType], #encode(float)) == 0) {
return [NSString stringWithFormat:#"%0.1f} ",num.floatValue];
} else if (strcmp([data objCType], #encode(double)) == 0) {
return [NSString stringWithFormat:#"%0.1f} ",num.doubleValue];
} else if (strcmp([data objCType], #encode(int)) == 0) {
return [NSString stringWithFormat:#"%d} ",num.intValue];
} else if (strcmp([data objCType], #encode(BOOL)) == 0) {
return num.boolValue ? #"Yes} " : #"No} ";
} else if (strcmp([data objCType], #encode(long)) == 0) {
return [NSString stringWithFormat:#"%ld} ",num.longValue];
}
}

Resources