swebal
swebal

Reputation: 441

Parse doubles such as 1.0 from JSON in Swift 4 without loosing the decimal?

or can I check if a number was decoded as a decimal number and not and integer later?

if let int = any as? Int {
  print("Object in an integer")        
} else if let num = any as? Double {
  print("Object in a double")
}

, where "any" is an Any value and = 1.0 (not a string) in the JSON file. "any" can be cast to both integer and double (so the order of which I check determines the outcome), but I would like to keep the original format from the JSON file.

Decoding is done using the following line:

let json = try JSONSerialization.jsonObject(with: data, options: [])

Edit: I've tried checking CFType, but get the same for both 1 and 1.0 (inspired by http://stackoverflow.com/a/30223989/1694526)

Any ideas?

Upvotes: 2

Views: 3081

Answers (2)

Oliver Pearmain
Oliver Pearmain

Reputation: 20590

The solution is to parse to an NSNumber and then to a Decimal (or NSDecimalNumber). DO NOT parse via a Double.

let jsonString = "[ 4.01 ]"
let jsonData = jsonString.data(using: .utf8)!
let jsonArray = try! JSONSerialization.jsonObject(with: jsonData, options: []) as! [Any]

// This is the WRONG way to parse decimals (via a Double)
// parseAttemptA = 4.009999999999998976
let parseAttemptA: Decimal = Decimal(jsonArray[0] as! Double)

// This is the CORRECT way to parse decimals (via an NSNumber)
// parseAttemptB = 4.01
let parseAttemptB: Decimal = (jsonArray[0] as! NSNumber).decimalValue

Here's a screenshot of a playground...

Correct Parsing of Decimal in Swift

Upvotes: 2

Matic Oblak
Matic Oblak

Reputation: 16774

As already mentioned by @Sulthan this is not possible on the level you are working as JSONSerialization will and should use a single class to represent a value and may not determine its type.

You could try finding some other tool to check for values but does it really make sense?

  1. You are trying to look for differences between Int and Double but what about 64 or 32 bit? Or signed and unsigned? We usually don't write those into strings so there really is no way to distinguish between them. So there is really no general logic in doing so.
  2. Are you sure the returned JSON will always have ".0" appended for these values? This really depends on the system and a smallest optimization would trim that because JSON standard does not include precisions on numbers. For instance if I use JSONSerialization and print out String(data: (try! JSONSerialization.data(withJSONObject: [ "value": 1.0 ], options: .prettyPrinted)), encoding: .utf8) I receive: {\n \"value\" : 1\n} which means it trimmed ".0" anyway.
  3. I find it hard to understand how this would be good structurally. If you need to save these data for instance into your database you will need to define the size and type of the primitive to hold your data. If you need to use some arithmetics you again need to specify the type...

The only way would be to use it as a display string. But in that case your value should be returned as a string and not as a number.

Upvotes: 2

Related Questions