2013-08-01 30 views
11

Lấy đoạn mã sau:NSJSONSerialization deserialize numbers là NSDecimalNumber?

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

Câu hỏi của tôi là, là results[@"foo"] một NSDecimalNumber, hoặc một cái gì đó với độ chính xác nhị phân hữu hạn giống như một đôi hoặc nổi? Về cơ bản, tôi có một ứng dụng đòi hỏi độ chính xác mất mát đi kèm với một NSDecimalNumber và cần đảm bảo rằng việc deserialization JSON không dẫn đến làm tròn vì đôi/nổi etcetera.

Ví dụ: nếu nó được hiểu như là một phao, tôi muốn chạy vào các vấn đề như thế này với độ chính xác:

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

tôi đã cố gắng giải thích foo như một NSDecimalNumber và in kết quả:

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

Nhưng sau đó tôi thấy rằng gọi stringValue trên một NSDecimalNumber không in tất cả các chữ số có nghĩa dù sao, ví dụ ..

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

... để in fooAsDecimal không cho tôi biết liệu results[@"foo"] có được làm tròn ở độ chính xác hữu hạn bởi trình phân tích cú pháp JSON hay không.

Để rõ ràng, tôi nhận thấy tôi có thể sử dụng chuỗi thay vì một số trong biểu diễn JSON để lưu giá trị của foo, tức là "0.1" thay vì 0.1 và sau đó sử dụng [NSDecimalNumber decimalNumberWithString:results[@"foo"]]. Nhưng, những gì tôi quan tâm là làm thế nào lớp NSJSONSerialization deserializes số JSON, vì vậy tôi biết cho dù điều này là thực sự cần thiết hay không.

Trả lời

2

Để trả lời câu hỏi trong tiêu đề: Không, nó không tạo ra các đối tượng NSNumber. Bạn có thể dễ dàng kiểm tra điều này:

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]); 

sẽ in __NSCFNumber.

Bạn có thể chuyển đổi mà NSNumber đối tượng đến một NSDecimalNumber với [NSDecimalNumber decimalNumberWithDecimal:[number decimalValue]], nhưng theo các tài liệu cho decimalValue

Giá trị trả lại không được đảm bảo để được chính xác cho phao và giá trị tăng gấp đôi.

3

Câu trả lời ngắn gọn là bạn không nên tuần tự hóa thành JSON nếu bạn yêu cầu mức chính xác NSDecimalNumber. JSON chỉ có một định dạng số: gấp đôi, có độ chính xác kém hơn đối với NSDecimalNumber.

Câu trả lời dài, chỉ vì lợi ích học thuật, bởi vì câu trả lời ngắn cũng là câu trả lời đúng, là "Không nhất thiết". NSJSONSerialization đôi khi deserialize như NSDecimalNumber, nhưng nó là không có giấy tờ, và tôi đã không xác định, những gì tập hợp các trường hợp theo đó nó làm được. Ví dụ:

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 =ull; 

    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]; 

và trong debugger:

(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 

Vì vậy, hãy của những gì bạn sẽ làm.Bạn chắc chắn không nên giả định rằng nếu bạn tuần tự hóa một NSDecimalNumber thành JSON, bạn sẽ nhận được một số NSDecimalNumber.

Nhưng, một lần nữa, bạn không nên lưu trữ NSDecimalNumbers trong JSON.

+5

Điều này không đúng. Định dạng JSON không quan tâm đến độ chính xác của float hoặc double. Đó là trình phân tích cú pháp JSON mất độ chính xác. – Sulthan

+0

@icodestuff: nếu tôi chạy mã của bạn bằng cách sử dụng 'double dl = 8.92918e-128;', 'dict_back' là' nil'. Bạn có bất kỳ ý tưởng nào nếu có một cách để lấy JSON deserialisation để làm việc cho các giá trị như vậy bằng cách sử dụng 'NSJSONSerialization' hoặc các thư viện khác? – sergio

+0

Như Sulthan đã nói, JSON không quan tâm đến độ chính xác. Nếu API của Apple tuân theo các thông số kỹ thuật, nó sẽ sử dụng NSDecimalNumber. – xtravar

4

NSJSONSerialization (và JSONSerialization trong Swift) theo mô hình chung:

  1. Nếu một số chỉ có một phần số nguyên (không thập phân hoặc số mũ), nỗ lực để phân tích nó như một long long. Nếu điều đó không tràn, hãy trả lại NSNumber với long long.
  2. Cố gắng phân tích cú đúp bằng strtod_l. Nếu nó không tràn, hãy trả lại NSNumber với double.
  3. Trong tất cả các trường hợp khác, hãy cố gắng sử dụng NSDecimalNumber hỗ trợ một phạm vi giá trị lớn hơn nhiều, cụ thể là một giá trị chú giải lên đến 38 chữ số và số mũ giữa -128 ... 127.

Nếu bạn xem các ví dụ khác mọi người đã đăng, bạn có thể thấy khi giá trị vượt quá phạm vi hoặc độ chính xác của double bạn nhận được NSDecimalNumber quay lại.

1

Tôi đã có cùng một vấn đề, ngoại trừ tôi đang sử dụng Swift 3. Tôi đã thực hiện a patched version of the JSONSerialization class phân tích tất cả các số là Decimal. Nó chỉ có thể phân tích cú pháp/deserialize JSON, nhưng không có bất kỳ mã tuần tự hóa nào. Nó dựa trên việc thực hiện lại nguồn mở của Apple Foundation trong Swift.

Các vấn đề liên quan