带小十进制数的NSJSONSerialization

时间:2016-01-28 05:36:25

标签: json swift

我尝试在Double生成的某些JSON中包含0.81 NSJSONSerializationlet jsonInput = [ "value": 0.81 ] let data = try NSJSONSerialization.dataWithJSONObject(jsonInput, options: NSJSONWritingOptions.PrettyPrinted) let json = NSString(data: data, encoding: NSUTF8StringEncoding)! print( json ) 。代码如下:

{
  "value" : 0.8100000000000001
}

输出结果为:

{
  "value" : 0.81
}

但我希望看到的是:

NSJSONSerialization

如何让Double执行此操作?

另一件令我困惑的事情是Swift对64位let eightOne:Double = 0.81 "\(eightOne)" print( eightOne ) 的处理。在操场上我也可以这样做:

0.81

然后根据需要输出:

eightOne

即使在操场上,就内部表示而言,0.8100000000000001显示为BigDecimals。然而,当它转换为字符串时,它会切断其余部分。

我确实已经解决了这个问题,因为您需要对任何类型的财务处理进行排序(例如,在Java中,我们知道在财务价值方面我们只使用NSString( format: "%\(0.2)f", 0.81)

请帮忙。 :)

注意:此处的重点是序列化为JSON。不只是简单地致电dynamicType

3 个答案:

答案 0 :(得分:2)

用于精确的base-10算术(最多38位有效数字) 你可以使用NSDecimalNumber

let jsonInput = [ "value":  NSDecimalNumber(string: "0.81") ]

let val = NSDecimalNumber(integer: 81).decimalNumberByDividingBy(NSDecimalNumber(integer: 100))
let jsonInput = [ "value":  val ]

然后

let data = try NSJSONSerialization.dataWithJSONObject(jsonInput, options: NSJSONWritingOptions.PrettyPrinted)
let json = NSString(data: data, encoding: NSUTF8StringEncoding)!
print( json )

产生输出

{
  "value" : 0.81
}

答案 1 :(得分:0)

手动转换

您需要将Double转换为Decimal以在序列化时保留其预期的字符串表示形式。一种方法是:

(0.81 as NSNumber).decimalValue

自动转换的JSONSerialization扩展

要自动和递归地对JSON对象中的所有Double值执行此操作,无论是Dictionary还是Array,您都可以使用:

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 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 = n.decimalValue
        } 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)
    }
}

用法

JSONSerialization.decimalData(withJSONObject: [ "value": 0.81 ], options: [])

注意

如果您需要对小数格式进行微调,可以在Specify number of decimals when serializing currencies with JSONSerialization上查看Eneko Alonso的答案。

答案 2 :(得分:0)

如果您使用了' NSDecimalNumber'需求时,建议封装以方便使用并减少错误。

这里是Demo供您参考,使用简单。希望可以帮助您!

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