I am getting float value in string format from JSON like below
JSON:
{
"result": {
"conversion_factor": {
"conv_factor": "0.820000"
}
"search_result": [
{
"service_details": [
{
"standard_rate_in_usd": "10.00",
now i need to multiply conv_factor * standard_rate_in_usd
code: with this code only first value coming correct remaining showing wrong why?
let indexData = searchResult?.result?.search_result?[indexPath.row]
if let servDetls = indexData?.service_details{
for oneSerdet in servDetls{
var stringPrice: String?
if var priceVal = Float(oneSerdet.standard_rate_in_usd!), var convVal = Float((searchResult?.result?.conversion_factor?.conv_factor)!) {
stringPrice = String(priceVal * convVal)
}
else{
stringPrice = ""
}
cell.priceLbl.text = "£ \(stringPrice ?? "") \(currData)"
}
}
You need to convert to floats:
let convResult = Float(oneSerdet.standard_rate_in_usd) *
Float(searchResult?.result?.conversion_factor?.conv_factor)
Try this, hope this will resolves your issue
if let conv_factor = oneSerdet.conv_factor,
let factor = Double(conv_factor),
let standard_rate_in_usd = oneSerdet.standard_rate_in_usd,
let usd = Double(standard_rate_in_usd) {
print(factor*usd)
cell.priceLbl.text = "\(factor*usd)"
}else{
cell.priceLbl.text = ""
print("Unreachable")
}
You have multiple problems:
Your construct searchResult?.result?.conversion_factor?.conv_factor evaluates to an Optional String: String?.
You can't multiply strings. Strings are not numeric. You need to convert your string to a Double. But the Double initializer that takes a string also returns an Optional (since "Apple", for example, can't be converted to a Double.
Here is some sample code that will take 2 Optional Strings, try to convert them to Doubles, and multiples them.
let xString: String? = "123.456"
let yString: String? = "2.0"
let x = xString.map { Double($0) ?? 0 } ?? 0.0
let y = yString.map { Double($0) ?? 0 } ?? 0.0
let result: Double = x * y
print(result)
That outputs
246.912
Related
I need to store each part of the json into a data structure, maybe Array?
This is what I have when I call Firebase realtime:
ref.observeSingleEvent(of: .value, with: { (snapshot) in
for child in snapshot.children {
let snap = child as! DataSnapshot
let key = snap.key
let value = snap.value
print("######################################################\n\n")
print("key = \(key) value = \(value!)")
}
})
when printed it looks like this:
What would be the best way to accomplish this?
I need to store the title, description and postUrl
EDIT:
If I do this:
let userID = Auth.auth().currentUser?.uid
ref.child("post").child(userID!).observeSingleEvent(of: .value, with: { (snapshot) in
// Get user value
print(snapshot)
}) { (error) in
print(error.localizedDescription)
}
that would give me the snapshot id but not the values because the user is not in that DB, how can I print the snapshots without saying current user?
If you can't for some reason access the snapshot's value parameter as above, you will probably need to manually parse the value string (as it's not proper json). The code below will allow you to initialise the data structure with the value text. This assumes value is a nicely formed string as in the example below. If not you may need to play with it a bit more:
let valueString = """
{
description = dawdled
postUrl = http://darrengillman.com
timestamp = 1572735278831
title = testy
}
"""
struct DataStruct {
let decription: String
let postUrl: URL
let timestamp: Int
let test: String
init?(from value: String) {
let strings = Array(value.components(separatedBy: .newlines).dropLast().dropFirst()).map{$0.components(separatedBy: "=")}.map{$0.dropFirst().first!.trimmingCharacters(in: .whitespaces)}
guard strings.count == 4 else {return nil}
self.decription = strings[0]
self.postUrl = URL(string: strings[1])!
self.timestamp = Int(strings[2]) ?? 0
self.test = strings[3]
}
}
let myData = DataStruct(from: valueString)
("Global_Data"): {
Created = 574049501;
Domain = "";
Expires = "2019-03-19 02:11:40 +0000";
Name = "Data";
Path = "/";
Value = "%7B%22countryISO%22%3A%22US%22%2C%22cultureCode%22%3A%22en-GB%22%2C%22currencyCode%22%3A%22USD%22%2C%22apiVersion%22%3A%222.1.4%22%7D;
Version = 1;
}
On grabbing a Cookie data from a webview i got the Value => as a string
with some characters knowing these characters represent special characters or letters.
How can i convert it to a JSON format. Thanks
"%7B%22countryISO%22%3A%22US%22%2C%22cultureCode%22%3A%22en-GB%22%2C%22currencyCode%22%3A%22USD%22%2C%22apiVersion%22%3A%222.1.4%22%7D"
Your string is already a JSON string you just have to remove the percent encoding from it, create a custom structure that conform to Decodable and you are all set:
struct Root: Decodable {
let countryISO, cultureCode, currencyCode, apiVersion: String
}
let string = "%7B%22countryISO%22%3A%22US%22%2C%22cultureCode%22%3A%22en-GB%22%2C%22currencyCode%22%3A%22USD%22%2C%22apiVersion%22%3A%222.1.4%22%7D"
let json = string.removingPercentEncoding ?? ""
"{"countryISO":"US","cultureCode":"en-GB","currencyCode":"USD","apiVersion":"2.1.4"}"
do {
let root = try JSONDecoder().decode(Root.self, from: Data(json.utf8))
print(root.countryISO) // "US"
print(root.cultureCode) // "en-GB"
print(root.currencyCode) // "USD"
print(root.apiVersion) // "2.1.4"
} catch {
print(error)
}
I'm struggling to find a method of looping through JSON data to find string matches. I have over 90 values in each row so I don't want to use the row[""] method I have commented out.
I get the error: Type 'Element' (aka 'AnyObject') does not conform to protocol 'SequenceType'.
So I assume this is because the JSON data doesn't work with the the
"for (key, value) in row" method I am using. All my searching has just suggested the use of row[""].
Any help would be greatly appreciated!
func searchBar(searchBar: UISearchBar, textDidChange searchText: String) {
for row in result {
for (key,value) in row {
//let value = row["city"] as! String
if value.lowercaseString.containsString(searchText.lowercaseString){
print("\(value) matches \(searchText)")
}
}
}
}
Fragment of data (beginning and end):
{
0 = 469347;
1 = 20;
10 = "2015-09-16";
11 = "2015-09-18";
12 = 1;
13 = 6;
14 = "$250 free fuel against receipts";
15 = "";
....
transmission = Manual;
tv = 0;
"tyre_preassure" = "";
"unique_key" = 98fd557e5988ea0d66cfaf9cf2742789;
"vehicle_type" = "3 Berth Hitop";
"vehicle_type_id" = 219;
"waste_water" = "";
width = "";
year = "";
}
{
...
}
Assuming your dictionary only contains String for every key value, I don't think you need a nested loop to do this. Just try this
for (key,value) in result {
let myString = value as! String
if myString.lowercaseString.containsString(searchText.lowercaseString){
print("\(myString) matches \(searchText)")
}
}
I'm attemptin to include the Double value of 0.81 in some JSON generated by NSJSONSerialization. The code is as follows:
let jsonInput = [ "value": 0.81 ]
let data = try NSJSONSerialization.dataWithJSONObject(jsonInput, options: NSJSONWritingOptions.PrettyPrinted)
let json = NSString(data: data, encoding: NSUTF8StringEncoding)!
print( json )
The output is:
{
"value" : 0.8100000000000001
}
But what I'd like to see is:
{
"value" : 0.81
}
How can I make NSJSONSerialization do this?
One further thing that is confusing me here is Swift's handling of the 64bit Double. As in the playground I can also do this:
let eightOne:Double = 0.81
"\(eightOne)"
print( eightOne )
And the output is then as desired with:
0.81
Even though in the playground it shows eightOne as 0.8100000000000001 as far as internal representation goes. However here when it converts to string it chops off the rest.
I'm surely this is solved, as you'd need it sorted for any kind of financial handling (eg. in Java we know we only use BigDecimals when it comes to financial values).
Please help. :)
NOTE: The focus here is on serialization to JSON. Not just a simple call off to NSString( format: "%\(0.2)f", 0.81).
For precise base-10 arithmetic (up to 38 significant digits)
you can use NSDecimalNumber:
let jsonInput = [ "value": NSDecimalNumber(string: "0.81") ]
or
let val = NSDecimalNumber(integer: 81).decimalNumberByDividingBy(NSDecimalNumber(integer: 100))
let jsonInput = [ "value": val ]
Then
let data = try NSJSONSerialization.dataWithJSONObject(jsonInput, options: NSJSONWritingOptions.PrettyPrinted)
let json = NSString(data: data, encoding: NSUTF8StringEncoding)!
print( json )
produces the output
{
"value" : 0.81
}
Manual conversion
You'll need to convert your Double to a Decimal to keep its expected string representation when serializing.
One way to avoid a precision of 16 digits may be to round with a scale of 15:
(0.81 as NSDecimalNumber).rounding(accordingToBehavior: NSDecimalNumberHandler(roundingMode: .plain, scale: 15, raiseOnExactness: false, raiseOnOverflow: true, raiseOnUnderflow: true, raiseOnDivideByZero: true)) as Decimal
JSONSerialization extension for automatic conversion
To automatically and recursively do it for all Double values in your JSON object, being it a Dictionary or an Array, you can use:
import Foundation
/// https://stackoverflow.com/q/35053577/1033581
extension JSONSerialization {
/// Produce Double values as Decimal values.
open class func decimalData(withJSONObject obj: Any, options opt: JSONSerialization.WritingOptions = []) throws -> Data {
return try data(withJSONObject: decimalObject(obj), options: opt)
}
/// Write Double values as Decimal values.
open class func writeDecimalJSONObject(_ obj: Any, to stream: OutputStream, options opt: JSONSerialization.WritingOptions = [], error: NSErrorPointer) -> Int {
return writeJSONObject(decimalObject(obj), to: stream, options: opt, error: error)
}
fileprivate static let roundingBehavior = NSDecimalNumberHandler(roundingMode: .plain, scale: 15, raiseOnExactness: false, raiseOnOverflow: true, raiseOnUnderflow: true, raiseOnDivideByZero: true)
fileprivate static func decimalObject(_ anObject: Any) -> Any {
let value: Any
if let n = anObject as? [String: Any] {
// subclassing children
let dic = DecimalDictionary()
n.forEach { dic.setObject($1, forKey: $0) }
value = dic
} else if let n = anObject as? [Any] {
// subclassing children
let arr = DecimalArray()
n.forEach { arr.add($0) }
value = arr
} else if let n = anObject as? NSNumber, CFNumberGetType(n) == .float64Type {
// converting precision for correct decimal output
value = NSDecimalNumber(value: anObject as! Double).rounding(accordingToBehavior: roundingBehavior)
} else {
value = anObject
}
return value
}
}
private class DecimalDictionary: NSDictionary {
let _dictionary: NSMutableDictionary = [:]
override var count: Int {
return _dictionary.count
}
override func keyEnumerator() -> NSEnumerator {
return _dictionary.keyEnumerator()
}
override func object(forKey aKey: Any) -> Any? {
return _dictionary.object(forKey: aKey)
}
func setObject(_ anObject: Any, forKey aKey: String) {
let value = JSONSerialization.decimalObject(anObject)
_dictionary.setObject(value, forKey: aKey as NSString)
}
}
private class DecimalArray: NSArray {
let _array: NSMutableArray = []
override var count: Int {
return _array.count
}
override func object(at index: Int) -> Any {
return _array.object(at: index)
}
func add(_ anObject: Any) {
let value = JSONSerialization.decimalObject(anObject)
_array.add(value)
}
}
Usage
JSONSerialization.decimalData(withJSONObject: [ "value": 0.81 ], options: [])
Note
If you need fine tuning of decimal formatting, you can check Eneko Alonso answer on Specify number of decimals when serializing currencies with JSONSerialization.
If you have use 'NSDecimalNumber' demand, it is suggested that encapsulate for ease of use and reduce mistakes.
Here's the Demo for you reference, using a simple.The hope can help you!
switch (operatorType) {
case 0:
resultNumber = SNAdd(_cardinalNumberTextField.text, _complementNumberTextField.text);
break;
case 1:
resultNumber = SNSub(_cardinalNumberTextField.text, _complementNumberTextField.text);
break;
case 2:
resultNumber = SNMul(_cardinalNumberTextField.text, _complementNumberTextField.text);
break;
case 3:
resultNumber = SNDiv(_cardinalNumberTextField.text, _complementNumberTextField.text);
break;
}
Github:https://github.com/ReverseScale/DecimalNumberDemo
i would like to ask a little help.
I have a method which call a webservice and get a json object from it.
it's look like this way:
func wsServiceFeedTst() {
println("WS called...")
println("tstFrames count: " + tstFrames.count.description)
let json = JSON(url:"http://79.172.249.175:7001/RestWebServiceApp/webresources/entity.bkkkallerfeedtst")
println(json)
for (k, v) in json["bkkKallerFeedTst"] {
let dateShow : NSDate? = v["feedDate"].asDate
var finalFormatter = NSDateFormatter()
finalFormatter.dateFormat = "yyyy.MM.dd - HH:mm"
let finalDate = finalFormatter.stringFromDate(dateShow!)
tstFrames.append(TimeFrame(text: v["feedText"].description, date: finalDate, image: nil, routeName: v["feedRouteShName"].description, postId: v["id"].description,routType: v["feedImgType"].description))
}
}
After i got the json i'm trying to iterate on it and try to add the nodes of the json to another array but when my json contains only one element it's failed because my json is not an array, this is the line where it's throws exception:
let finalDate = finalFormatter.stringFromDate(dateShow!)
it is waiting for optional, but it get nil caused by this line:
let dateShow : NSDate? = v["feedDate"].asDate
this is how my json looks like when it has only one element:
{"bkkKallerFeedTst":{"feedRouteShName":"143","id":"348","feedLat":"47.5998971180592","feedImgType":"3","feedDate":"2015-06-15T14:07:30+02:00","feedLon":"19.0457082953807","feedText":"Itthon :)”}}
And this is how it looks like when it has more then one elements (now it has two element)
{"bkkKallerFeedTst":[{"feedRouteShName":"H5","id":"349","feedLat":"47.5535475845461","feedImgType":"2","feedDate":"2015-06-15T15:27:02+02:00","feedLon":"19.0458004338391","feedText":"Hév ;)"},{"feedRouteShName":"143","id":"348","feedLat":"47.5998971180592","feedImgType":"3","feedDate":"2015-06-15T14:07:30+02:00","feedLon":"19.0457082953807","feedText":"Itthon :)"}]}
Does anybody has any ide about how to solve this?
Thank you very much!
By the answer i create this:
var bkkKallerFeedTst = json["bkkKallerFeedTst"]
var bkkKallerFeedTstArray : [[NSObject : AnyObject]]
bkkKallerFeedTstArray = []
if bkkKallerFeedTst.isDictionary {
bkkKallerFeedTstArray.append(bkkKallerFeedTst.asDictionary!)
} else {
}
for feed in bkkKallerFeedTstArray {
println(feed["feedRouteShName"]) //now its printing: Optional(143)
}
now it's printing this:
[feedImgType: 3, feedRouteShName: 143, feedLat: 47.5998971180592, feedText: Itthon :), feedLon: 19.0457082953807, id: 348, feedDate: 2015-06-15T14:07:30+02:00]
UPDATE: this is the solution..
var bkkKallerFeedTst = json["bkkKallerFeedTst"]
var bkkKallerFeedTstArray : [JSON]
if bkkKallerFeedTst.isDictionary {
bkkKallerFeedTstArray = [bkkKallerFeedTst] //initialize
} else {
bkkKallerFeedTstArray = bkkKallerFeedTst.asArray!
}
for bkk in bkkKallerFeedTstArray {
let dateShow : NSDate = bkk["feedDate"].asDate!
var finalFormatter = NSDateFormatter()
finalFormatter.dateFormat = "yyyy.MM.dd - HH:mm"
let finalDate = finalFormatter.stringFromDate(dateShow)
tstFrames.append(TimeFrame(text: bkk["feedText"].description, date: finalDate, image: nil, routeName: bkk["feedRouteShName"].description, postId: bkk["id"].description,routType: bkk["feedImgType"].description))
}
It would probably be best to define the JSON object initially so it produces an array with a single value, rather than this way, but otherwise you could check to see if you get a valid date, and if you don't try it another way.
But looking at the API you've used, here https://github.com/dankogai/swift-json, it seems you can do checking to see whether you get a dictionary or an array. So I would cast the dictioanry value to a clearly typed variable for clarity using the '.isDictionary' method.
e.g. something akin to
var bkkKallerFeedTst = json["bkkKallerFeedTst"]
var bkkKallerFeedTstArray : [JSON]
if bkkKallerFeedTst.isDictionary {
bkkKallerFeedTstArray = [bkkKallerFeedTst] //initialize
} else {
bkkKallerFeedTstArray = bkkKallerFeedTst.asArray
}
May not be this exact code - I don't have the api.
Then you can iterate first through the array (for ? in bkkKallerFeedTstArray), then inside through the dictionary contained (as you were doing before)
Basically make sure you have an array of dictionaries first, before doing the operations.
Example with your code:
func wsServiceFeedTst() {
println("WS called...")
println("tstFrames count: " + tstFrames.count.description)
let json = JSON(url:"http://79.172.249.175:7001/RestWebServiceApp/webresources/entity.bkkkallerfeedtst")
println(json)
var bkkKallerFeedTst = json["bkkKallerFeedTst"]
var bkkKallerFeedTstArray : [JSON]
if bkkKallerFeedTst.isDictionary {
bkkKallerFeedTstArray = [bkkKallerFeedTst] //initialize
} else {
bkkKallerFeedTstArray = bkkKallerFeedTst.asArray
}
for bkk in bkkKallerFeedTstArray {
let dateShow : NSDate = bkk["feedDate"].asDate!
var finalFormatter = NSDateFormatter()
finalFormatter.dateFormat = "yyyy.MM.dd - HH:mm"
let finalDate = finalFormatter.stringFromDate(dateShow)
tstFrames.append(TimeFrame(text: v["feedText"].description, date: finalDate, image: nil, routeName: v["feedRouteShName"].description, postId: v["id"].description,routType: v["feedImgType"].description))
}
}