2012-03-28 25 views
14

Dưới đây là các mã:NSJSONSerialization không tạo container có thể thay đổi

NSError *parseError; 
NSMutableArray *listOfObjects = [NSJSONSerialization JSONObjectWithData:[@"[]" dataUsingEncoding:NSUTF8StringEncoding] options:NSJSONReadingMutableContainers error:&parseError]; 
NSLog(@"Is mutable? %li", [listOfObjects isKindOfClass:[NSMutableArray class]]); 

listOfObjects = [NSJSONSerialization JSONObjectWithData:[@"[[],{}]" dataUsingEncoding:NSUTF8StringEncoding] options:NSJSONReadingMutableContainers error:&parseError]; 
NSLog(@"Is mutable? %li", [listOfObjects isKindOfClass:[NSMutableArray class]]); 

Như bạn thấy, tôi kêu gọi chính xác cùng một phương pháp để phân tích cú pháp JSON cả hai lần, một với một danh sách trống trong JSON, và sau đó một danh sách với một đối tượng bên trong. Đây là kết quả:

Is mutable? 0 
Is mutable? 1 

Vấn đề là NSJSONSerialization dường như không theo tùy chọn tạo vùng chứa có thể thay đổi cho danh sách trống. Có vẻ như một lỗi đối với tôi, nhưng có lẽ tôi chỉ hiểu lầm mọi thứ.

Bất kỳ ý tưởng nào?

+0

Bạn có chắc chắn rằng họ không sắp ra mắt như NSMutableDictionary? – Zalykr

+0

Đầu ra khi bạn chạy 'po [[listOfObjects class] superclass]' trong bảng điều khiển gỡ lỗi là gì? – warrenm

+1

Tôi có thể xác nhận vấn đề này trên Mac OS X 10.7 - chỉ các mảng trống dường như bị ảnh hưởng. Nó dường như được sửa trong 10.8. – blutfink

Trả lời

13

này chỉ hoạt động như mong đợi:

NSString *s = @"{ \"objs\": [ \"a\", \"b\" ] }";  
NSData *d = [NSData dataWithBytes:[s UTF8String] length:[s length]]; 
id dict = [NSJSONSerialization JSONObjectWithData:d options:NSJSONReadingMutableContainers error:NULL]; 

NSLog(@"%@", dict); 

[[dict objectForKey:@"objs"] addObject:@"c"]; 

NSLog(@"%@", dict); 
NSLog(@"%@", [[dict objectForKey:@"objs"] class]); 

Dưới đây là giao diện điều khiển đầu ra:

2012-03-28 13:49:46.224 ExampleRunner[42526:707] { 
    objs =  (
     a, 
     b 
    ); 
} 
2012-03-28 13:49:46.225 ExampleRunner[42526:707] { 
    objs =  (
     a, 
     b, 
     c 
    ); 
} 
2012-03-28 13:49:46.225 ExampleRunner[42526:707] __NSArrayM 

EDIT

Lưu ý rằng nếu chúng ta thêm dòng sau vào mã ở trên .. .

NSLog(@"%@", [[dict objectForKey:@"objs"] superclass]); 
.210

... chúng tôi nhận được đầu ra sau trên console:

2012-03-28 18:09:53.770 ExampleRunner[42830:707] NSMutableArray 

... chỉ trong trường hợp nó không phải là rõ ràng rằng __NSArrayM là một lớp con riêng của NSMutableArray, do đó chứng minh rằng mã của OP đã thực sự làm việc như mong đợi (ngoại trừ tuyên bố NSLog).

EDIT

Oh, và bằng cách này, các dòng mã sau đây ...

NSLog(@"%d", [[dict objectForKey:@"objs"] isKindOfClass:[NSMutableArray class]]); 

... kết quả trong giao diện điều khiển đầu ra sau đây:

2012-03-28 18:19:19.721 ExampleRunner[42886:707] 1 

EDIT (trả lời câu hỏi đã thay đổi)

Thú vị ... trông giống như một lỗi. Căn cứ vào đoạn mã sau:

NSData *dictData2 = [@"{ \"foo\": \"bar\" }" dataUsingEncoding:NSUTF8StringEncoding]; 
id dict2 = [NSJSONSerialization JSONObjectWithData:dictData2 options:NSJSONReadingMutableContainers error:NULL]; 
NSLog(@"%@", [dict2 class]); 
NSLog(@"%@", [dict2 superclass]); 
NSLog(@"%d", [dict2 isKindOfClass:[NSMutableDictionary class]]); 

// This works... 
[dict2 setObject:@"quux" forKey:@"baz"]; 
NSLog(@"%@", dict2); 

NSData *dictData = [@"{}" dataUsingEncoding:NSUTF8StringEncoding]; 
id emptyDict = [NSJSONSerialization JSONObjectWithData:dictData options:NSJSONReadingMutableContainers error:NULL]; 
NSLog(@"%@", [emptyDict class]); 
NSLog(@"%@", [emptyDict superclass]); 
NSLog(@"%d", [emptyDict isKindOfClass:[NSMutableDictionary class]]); 

//...but this fails: 
[emptyDict setObject:@"quux" forKey:@"baz"]; 
NSLog(@"%@", emptyDict); 

Dưới đây là giao diện điều khiển đầu ra:

2012-03-29 09:40:52.781 ExampleRunner[43816:707] NSMutableDictionary 
2012-03-29 09:40:52.782 ExampleRunner[43816:707] 1 
2012-03-29 09:40:52.782 ExampleRunner[43816:707] __NSCFDictionary 
2012-03-29 09:40:52.782 ExampleRunner[43816:707] NSMutableDictionary 
2012-03-29 09:40:52.783 ExampleRunner[43816:707] 1 
2012-03-29 09:40:52.783 ExampleRunner[43816:707] { 
    baz = quux; 
    foo = bar; 
} 
2012-03-29 09:40:52.784 ExampleRunner[43816:707] __NSCFDictionary 
2012-03-29 09:40:52.784 ExampleRunner[43816:707] NSMutableDictionary 
2012-03-29 09:40:52.784 ExampleRunner[43816:707] 1 
2012-03-29 09:40:52.785 ExampleRunner[43816:707] NSException: -[__NSCFDictionary setObject:forKey:]: mutating method sent to immutable object 

mảng Vì vậy, sản phẩm nào và từ điển được tạo ra theo cách này dường như không hoạt động như mong đợi.

+0

[chi phí class] vẫn là một NSArray mặc dù, thay vì một mảng mutable, không? –

+0

Không. Đầu tiên, bạn không thể thêm một đối tượng vào một thể hiện của 'NSArray'. Thứ hai, nếu bạn nhìn vào đầu ra của giao diện điều khiển, mảng thực sự là một cá thể của '__NSArrayM', là một lớp con riêng của' NSMutableArray'. – jlehr

+0

Xin chào Cảm ơn bạn đã trả lời cho đến nay .. Tôi đã cập nhật câu hỏi, mặc dù tôi đã không giải thích được vấn đề chính xác .. bạn có thể kiểm tra lại không? –

1

Những người khác cũng thực hiện việc này cho một lỗi, xem

  1. https://github.com/couchbaselabs/TouchDB-iOS/issues/44 hoặc
  2. https://github.com/johnlabarge/jlbiosutils/blob/master/jlbiosutils/DynamicProperties.m, ví dụ.

Trong trường hợp sau, bạn có thể xem cách giải quyết hoàn chỉnh cũng cho từ điển trống (xem phương pháp DynamicGetter(...)).

+0

Bất cứ điều gì mà Jens Alfke coi là một lỗi là một lỗi đối với tôi. Cảm ơn các liên kết, tôi đã kết thúc việc sửa lỗi này khá nhiều theo cùng một cách. –

7

Dưới đây là cách giải quyết của tôi cho vấn đề này:

#import "NSJSONSerialization+MutableBugFix.h" 

@implementation NSJSONSerialization (NSJSONSerialization_MutableBugFix) 

+ (id)JSONObjectWithDataFixed:(NSData *)data options:(NSJSONReadingOptions)opt error:(NSError **)error { 
    id object = [NSJSONSerialization JSONObjectWithData:data options:opt error:error]; 

    if (opt & NSJSONReadingMutableContainers) { 
     return [self JSONMutableFixObject:object]; 
    } 

    return object; 
} 

+ (id)JSONMutableFixObject:(id)object { 
    if ([object isKindOfClass:[NSDictionary class]]) { 
     // NSJSONSerialization creates an immutable container if it's empty (boo!!) 
     if ([object count] == 0) { 
      object = [object mutableCopy]; 
     } 

     for (NSString *key in [object allKeys]) { 
      [object setObject:[self JSONMutableFixObject:[object objectForKey:key]] forKey:key]; 
     } 
    } else if ([object isKindOfClass:[NSArray class]]) { 
     // NSJSONSerialization creates an immutable container if it's empty (boo!!) 
     if (![object count] == 0) { 
      object = [object mutableCopy]; 
     } 

     for (NSUInteger i = 0; i < [object count]; ++i) { 
      [object replaceObjectAtIndex:i withObject:[self JSONMutableFixObject:[object objectAtIndex:i]]]; 
     } 
    } 

    return object; 
} 

@end 

Vì vậy, tôi gọi:

NSDictionary *object = [NSJSONSerialization JSONObjectWithDataFixed:jsonData options:NSJSONReadingMutableContainers error:&err]; 

Thay vì thông thường:

NSDictionary *object = [NSJSONSerialization JSONObjectWithData:jsonData options:NSJSONReadingMutableContainers error:&err]; 
+0

tuyệt vời! nên là câu trả lời được chấp nhận ... –

1

Dưới đây là những gì tôi làm:

BOOL needsWorkaround = YES; 
if (needsWorkaround) 
{ 
    NSMutableDictionary* appState2 = 
     (__bridge_transfer NSMutableDictionary*) 
     CFPropertyListCreateDeepCopy (
      kCFAllocatorDefault, (__bridge void*)appState, 
      kCFPropertyListMutableContainersAndLeaves 
     ); 
    appState = appState2; 
} 
Các vấn đề liên quan