SBJSON Float in NSDictionary written to NSString incorrect - ios

I'm using SBJSON in a project and I'm trying to build a json string from a dictionary.
Here's what's going on, I'm putting floats as NSNumbers into a dictionary:
NSDictionary* tempdic = [[NSDictionary alloc] init];
tempdic = [NSDictionary dictionaryWithObjectsAndKeys:productId, #"productId", [NSNumber numberWithFloat:quantity], #"aantal", price, #"price" , nil];
[orders addObject:tempdic];
NSDictionary* json = [NSDictionary dictionaryWithObjectsAndKeys: orders, #"order", message, #"message", [NSNumber numberWithFloat:orderprice], #"totalPrice",dueDate,#"dueDate", nil];
And then to finally write it as a json string, I tried these three.
1)
NSData* jsonData = [writer dataWithObject:json];
NSString* jsonString = [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding];
2)
NSString* jsonString = [json JSONRepresentation];
3)
NSString* jsonString = [writer stringWithObject:json];
Each of these changes 0.95 into 0.95000000000000034355 or even worse 0.949999999999999992344 or something alike.
Why is this happening? How can I prevent this?
Thanks

That's the basic problem with float values. You can't store values which can't be represented by the sum of the power of 2. Thus resulting with the approximate value to your floating point.
e.g. 1.25 can be easily represented as sum of power of 2
i.e. 1*2^0 +1*2^-2 but if you are going to represent 1.33 as sum of power of 2 then the resultant would be 1*2^0 + 1*2^-2 + 1*2^-4 + 1*2^-8 + 1*2^-9 ....
Just read Representable numbers, conversion and rounding on wiki.
And you can check your floating point representation using online tool.

This is the classic problem with floating point numbers. You can, however, use NSDecimalNumber rather than NSNumber to get higher precision (at the cost of lower range of numbers).

Related

What is the difference between #"1.5" and #(1.5) while setting the value in NSDictionary?

In iPhone project,
It was while I was while setting Value in dictionary,
NSMutableDictionary*dictionary=[[NSMutableDictionary alloc] init];
[dictionary setValue:#(2.8) forKey:#"Why"];
AND,
NSMutableDictionary*dictionary=[[NSMutableDictionary alloc] init];
[dictionary setValue:#"2.8" forKey:#"Why"];
My question is Why not #"2.5" and #(2.5) ?
You have two questions, it would be better to have a single question.
But as to the difference,#"2.5" is an NSString where #(2.5) is an NSNumber. There is a big difference between textual and numeric data.
As for why you need an NSNumber and not NSString is obvious: the kerning is a numeric value.
using the #() syntax you can box arbitrary C expressions. This makes it trivial to turn basic arithmetic calculations into NSNumber objects see below:
double x = 24.0;
NSNumber *result = #(x * .15);
NSLog(#"%.2f", [result doubleValue]);
You can also refer NSNumber object as #"" string but cant make calculations like above example. In your case both are acceptable but here calculation makes difference.
When you use
#"2.5" it's behave like a string
NSMutableDictionary*dictionary=[[NSMutableDictionary alloc] init];
[dictionary setValue:#"2.8" forKey:#"Why"];;
NSString *a = [dictionary ValueforKey:#"Why"];
but when you use #(2.8) then it's behave like a NSNumber
NSMutableDictionary*dictionary=[[NSMutableDictionary alloc] init];
[dictionary setValue:#(2.8) forKey:#"Why"];;
NSNumber *a = [dictionary ValueforKey:#"Why"];
#(2.8) is a type of NSNumber.
#"2.8" is a type of NSString.
Both the type and value were different between there two.

How to append a float value as float in dictionary?

I need to calculate a MD5 values with a json value to the server like,
{
"name":"swift",
"version":1.0,
"edition":1,
"date":"2014-12-26"
}
In this json the version needs to be send as float 1.0 not 1 and edition as integer 1.
So, I have tried to create a dictionary and done a NSJSONSerialization as,
NSDictionary *d = #{#"name":#"swift",
#"version":[NSDecimalNumber decimalNumberWithString:#"1.0"],
#"edition":[NSNumber numberWithInt:1],#"date":#"2014-12-26"};
NSData *jsonData = [NSJSONSerialization dataWithJSONObject:d options:0 error:nil];
But this is creating a json response as
{
"name":"swift",
"version":1,
"edition":1,
"date":"2014-12-26"
}
So the MD5 value is mismatching.
Can anyone help me with this to create a json with float value for "version" with decimal point (1.0) ?
There is nothing wrong with sending the numeric value 1.0 as simply 1 in JSON. Though you might have a little luck getting the .0 if you encoded a floating point in an NSNumber, vs using an NSDecimalNumber.
`... #"version":#(1.0)...`
The full dictionary:
NSDictionary *d = #{#"name":#"swift",
#"version":#(1.0),
#"edition":#(1),
#"date":#"2014-12-26"};
Create the NSMutableDictionary, convert the number to an NSNumber and put it into the NSMutableDictionary.
NSMutableDictionary *dict = [NSMutableDictionary dictionary];
float value = 1.0;
NSNumber *number = [NSNumber numberForFloat:value];
[dict setObject:number forKey:#"version"];
or using literals replace the above two lines with:
dict[#"version"] = #(value);

string handling in ios

I am getting String data from server in the format below. I want to get the value for every tag like phonenumber and name etc. I am able to convert it in array by comma separator. how to get individual values?
Company:Affiliated CO,Organization:TECHNICAL EDUCATION
SOCIETY,Organization:SINHGAD,Organization:National Basketball Association,Person:Parikshit N. Mahalle,PhoneNumber:81 98 22 416 316,PhoneNumber:9120-24100154,Position:Professor,SportsEvent:NBA.
Say your original string is stored in rawString.
You need to :
1) split the string by ,
NSArray *pieces = [rawString componentsSeparatedByString:#","];
2) for each item in this array, split it by :, and add it to a dictionary :
NSMutableDictionary *dict = [NSMutableDictionary new];
for (NSString *piece in pieces) {
NSArray *splitPiece = [piece componentsSeparatedByString:#":"];
// key is at splitPiece[0], value is at splitPiece[1]
dict[splitPiece[0]] = splitPiece[1];
}
Then you'll have a dictionary of what you wanted in the first place.
But as suggested in the comments, it would be far better (and more flexible) for you to receive JSON data.
Edit: your original string shows there are multiple fields named Organization. The code I've given is not designed to handle such cases, it's up to you to build upon it.
If this data is not being returned as a JSON object then you'll have to go with #Clyrille answer. But if it is JSON then NSJSONSerialization:JSONObjectWithData:options:error: will be the way to go.
EXAMPLE
NSDictionary *json = [NSJSONSerialization
JSONObjectWithData:/*urlResponse*/ options:0 error:nil];
NSString *company = [json objectForKey:#"Company"];
NSString *Organization = [json objectForKey:#"Organization"];
NSString *Person = [json objectForKey:#"Person"];
NSString *PhoneNumber = [json objectForKey:#"PhoneNumber"];
NSString *Position = [json objectForKey:#"Position"];
NSString *SportsEvent = [json objectForKey:#"SportsEvent"];

SBJson displays null

I am trying to parse some json data with SBJson to show the current temperature. The example code from this tutorial works perfect: Tutorial: Fetch and parse JSON
When I change the code to my json feed i get a null. I am kind of new to JSON but followed every tutorial and documentation I found. The json source i used: JSON Source
My code with sbjson:
NSString *responseString = [[NSString alloc] initWithData:responseData encoding:NSUTF8StringEncoding];
self.responseData = nil;
NSArray* currentw = [(NSDictionary*)[responseString JSONValue] objectForKey:#"current_weather"];
//choose a random loan
NSDictionary* weathernow = [currentw objectAtIndex:0];
//fetch the data
NSNumber* tempc = [weathernow objectForKey:#"temp_C"];
NSNumber* weatherCode = [weathernow objectForKey:#"weatherCode"];
NSLog(#"%# %#", tempc, weatherCode);
and of course I have already implemented the other sbjson code.
There is no current_weather key in the JSON data you posted. The structure is:
{ "data": { "current_condition": [ { ..., "temp_C": "7", ... } ], ... } }
Here's a visual representation:
Therefore, to get to temp_C, you'd need to first obtain the top-level data property:
NSDictionary* json = (NSDictionary*)[responseString JSONValue];
NSDictionary* data = [json objectForKey:#"data"];
then, from that, obtain the current_location property:
NSArray* current_condition = [data objectForKey:#"current_condition"];
and finally, from the current_location array, get the element you're interested in:
NSDictionary* weathernow = [current_condition objectAtIndex:0];
Also note that temp_C and weatherCode are strings, not numbers. To transform them to numbers, instead of:
NSNumber* tempc = [weathernow objectForKey:#"temp_C"];
NSNumber* weatherCode = [weathernow objectForKey:#"weatherCode"];
you could use something like:
int tempc = [[weathernow objectForKey:#"temp_C"] intValue];
int weatherCode = [[weathernow objectForKey:#"weatherCode"] intValue];
(or floatValue / doubleValue if the value is not supposed to be an int, but rather a float or a double)
You would then use %d (or %f for float / double) as a format string:
NSLog(#"%d %d", tempc, weatherCode);
Provided link returns json without current_weather parameter. There is only current_condition parameter, please review this.
Use NSJSONSerialization instead of JSONValue.
NSData* data = [responseString dataUsingEncoding:NSUTF8StringEncoding];
NSDictionary* jsonDict = [NSJSONSerialization
JSONObjectWithData:data
options:kNilOptions
error:&error];
NSLog(#"jsonDict:%#",jsonDict);
In your link, there is no current_weather key.
NSString* tempc = [[[[jsonDict objectForKey:#"data"] objectForKey:#"current_condition"] objectAtIndex:0] objectForKey:#"temp_C"];

NSNumber storing float. Please NO scientific notation?

My code looks like this
NSNumber *inputToNumber = [NSNumber numberWithFloat:[textField.text floatValue]];
the value from the textfield is actually a telephone number. It's stored in NSNumber as an annoying (2.0)78966e+08
How can I just get NSNumber to store it as 0207896608?
I think that the basic idea to store a phone number into a NSNumber is flawed:
how do you discriminate between numbers with or without leading 0 ?
how do you store phone numbers from foreign countries ?
I would use NSString in place of NSNumber.
Just because it's called a number doesn't mean a "telephone number" is a number in the same sense that "5" or "pi" are.
Either you should treat a telephone number as a string, or you should create a TelephoneNumber model class to represent each one.
Consider that there are places in the world where numbers don't have leading 0's and where a number with a leading 0 is not the same as the same number without a leading 0.
05843924 != 5843924
So stop being lazy with that NSNumber hacks and build your own phone-number class.
Scientific notation is used in may computer languages as the default output of very large (or very small) numbers. If you want the number to be output as a decimal, you need to specify the output format (the implementation varies by language.)
Also, julesjacobs is correct. You should not use FLOAT for a phone number as it is subject to binary rounding errors. Using INT or STRING will save you lots of headaches.
If you need to be able to deal with it as numbers maybe you should break it up into its parts, and store each part as an integer.
01112223333
country code 0
area code 111
prefix 222
number 3333
Or you could store the whole thing as a string if you don't need to manipulate it.
Are you storing a phone number in a float? You should consider using an integer or string. Perhaps:
NSNumber *inputToNumber = [NSNumber numberWithInt:[textField.text intValue]];
Hey Guys what do you think of this, It seems to full-fill my purposes. Only UK at the moment so will worry about localization when I get a chance.
I use this to get to store the number
NSNumber *inputToNumber = [NSNumber numberWithLongLong:(long long)[[textField.text stringByReplacingOccurrencesOfString:#" " withString:#""] longLongValue]];
And this method formats my telephone number and takes care of the preceeding 0 mentioned.
-(NSString *)phoneNumberString:(NSNumber *)phoneNumber {
//Add a zero because NSNumber won't save a preceeding zero
NSString *telephoneString = [[NSString alloc] initWithFormat:#"0%#", [phoneNumber stringValue]];
if (telephoneString.length >= 4) {
NSString *firstPart = [[NSString alloc] initWithString: [telephoneString substringToIndex:4]];
NSString *secondPart = [[NSString alloc] initWithString: [telephoneString substringFromIndex:4]];
//Add the two parts together with a space inbetween
NSString *formattedTelephoneString = [NSString stringWithFormat:#"%# %#", firstPart, secondPart];
//send it back to the cellForRow TableCell Method
[firstPart release];
[secondPart release];
[telephoneString release];
return formattedTelephoneString;
}
else {
return telephoneString;
}
}
Thanks for all the comments. I'm gonna mark the answer as whoever suggested NSString as I fear I will revert to using NSString for this instead of my above workaround.

Resources