NSJSONSerialization是否将数字反序列化为NSDecimalNumber?

时间:2013-08-01 06:12:43

标签: objective-c json serialization deserialization

采取以下代码:

NSError *error;
NSString *myJSONString = @"{ \"foo\" : 0.1}";
NSData *jsonData = [myJSONString dataUsingEncoding:NSUTF8StringEncoding];
NSDictionary *results = [NSJSONSerialization JSONObjectWithData:jsonData options:0 error:&error];

我的问题是,results[@"foo"]是一个NSDecimalNumber,还是具有有限二进制精度的东西,如double或float?基本上,我有一个应用程序需要NSDecimalNumber带来的无损准确性,并且需要确保JSON反序列化不会因为双精度/浮点数等而导致舍入。

E.g。如果它被解释为浮点数,我会遇到精确的问题:

float baz = 0.1;
NSLog(@"baz: %.20f", baz);
// prints baz: 0.10000000149011611938

我尝试将foo解释为NSDecimalNumber并打印结果:

NSDecimalNumber *fooAsDecimal = results[@"foo"];
NSLog(@"fooAsDecimal: %@", [fooAsDecimal stringValue]);
// prints fooAsDecimal: 0.1

但后来我发现在stringValue上调用NSDecimalNumber并不打印所有有效数字,例如......

NSDecimalNumber *barDecimal = [NSDecimalNumber decimalNumberWithString:@"0.1000000000000000000000000000000000000000000011"];
NSLog(@"barDecimal: %@", barDecimal);
// prints barDecimal: 0.1

...所以打印fooAsDecimal并没有告诉我JSON解析器是否在某些时候将results[@"foo"]舍入到有限精度。

要清楚,我意识到我可以使用字符串而不是JSON表示中的数字来存储foo的值,即"0.1"而不是0.1,然后使用{{1} }]。但是,我感兴趣的是NSJSONSerialization类如何反序列化JSON数字,所以我知道这是否真的有必要。

4 个答案:

答案 0 :(得分:6)

NSJSONSerialization(以及Swift中的JSONSerialization)遵循一般模式:

  1. 如果数字只有整数部分(无小数或指数),请尝试将其解析为long long。如果没有溢出,请使用NSNumber返回long long
  2. 尝试使用strtod_l解析双精度数。如果它没有溢出,请使用NSNumber返回double
  3. 在所有其他情况下,尝试使用支持更大范围值的NSDecimalNumber,特别是最多38位的尾数和-128 ... 127之间的指数。
  4. 如果您查看人们发布的其他示例,您可以看到当值超出double的范围或精度时,您会得到NSDecimalNumber

答案 1 :(得分:4)

简短的回答是,如果需要NSDecimalNumber级别的精度,则不应序列化为JSON。 JSON只有一种数字格式:double,它的精度低于NSDecimalNumber。

答案很长,只有学术兴趣,因为简短的答案也是正确的答案,是“不一定”。 NSJSONSerialization有时会反序列化为NSDecimalNumber,但它没有记录,我还没有确定,它所处理的环境是什么。例如:

    BOOL boolYes = YES;
    int16_t int16 = 12345;
    int32_t int32 = 2134567890;
    uint32_t uint32 = 3124141341;
    unsigned long long ull = 312414134131241413ull;
    double dlrep = 1.5;
    double dlmayrep = 1.1234567891011127;
    float fl = 3124134134678.13;
    double dl = 13421331.72348729 * 1000000000000000000000000000000000000000000000000000.0;
    long long negLong = -632414314135135234;
    unsigned long long unrepresentable = 10765432100123456789ull;

    dict[@"bool"] = @(boolYes);
    dict[@"int16"] = @(int16);
    dict[@"int32"] = @(int32);
    dict[@"dlrep"] = @(dlrep);
    dict[@"dlmayrep"] = @(dlmayrep);
    dict[@"fl"] = @(fl);
    dict[@"dl"] = @(dl);
    dict[@"uint32"] = @(uint32);
    dict[@"ull"] = @(ull);
    dict[@"negLong"] = @(negLong);
    dict[@"unrepresentable"] = @(unrepresentable);

    NSData *data = [NSJSONSerialization dataWithJSONObject:dict options:NSJSONWritingPrettyPrinted error:nil];

    NSDictionary *dict_back = (NSDictionary *)[NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableContainers error:nil];

并在调试器中:

(lldb) po [dict_back[@"bool"] class]
__NSCFBoolean
(lldb) po [dict_back[@"int16"] class]
__NSCFNumber
(lldb) po [dict_back[@"int32"] class]
__NSCFNumber
(lldb) po [dict_back[@"ull"] class]
__NSCFNumber
(lldb) po [dict_back[@"fl"] class]
NSDecimalNumber
(lldb) po [dict_back[@"dl"] class]
NSDecimalNumber
(lldb) po [dict_back[@"dlrep"] class]
__NSCFNumber
(lldb) po [dict_back[@"dlmayrep"] class]
__NSCFNumber
(lldb) po [dict_back[@"negLong"] class]
__NSCFNumber
(lldb) po [dict_back[@"unrepresentable"] class]
NSDecimalNumber

所以你要做的就是这样。你绝对不应该假设如果你将NSDecimalNumber序列化为JSON,你将得到一个NSDecimalNumber。

但是,再次,您不应该将NSDecimalNumbers存储在JSON中。

答案 2 :(得分:1)

要回答标题中的问题:不,它不会,它会创建NSNumber个对象。你可以轻松地测试一下:

NSArray *a = @[[NSDecimalNumber decimalNumberWithString:@"0.1"]];
NSData *data = [NSJSONSerialization dataWithJSONObject:a options:0 error:NULL];
a = [NSJSONSerialization JSONObjectWithData:data options:0 error:NULL];
NSLog(@"%@", [a[0] class]);

将打印__NSCFNumber

您可以使用NSNumberNSDecimalNumber个对象转换为[NSDecimalNumber decimalNumberWithDecimal:[number decimalValue]],但根据decimalValue

的文档
  

对于float和double值,返回的值不保证是精确的。

答案 3 :(得分:1)

我遇到了同样的问题,除了我使用Swift 3.我做了a patched version of the JSONSerialization class,将所有数字解析为Decimal' s。它只能解析/反序列化JSON,但没有任何序列化代码。它基于Apple在Swift中重新实现Foundation的开源。