NSJSONSerialization 不创建可变容器

Posted

技术标签:

【中文标题】NSJSONSerialization 不创建可变容器【英文标题】:NSJSONSerialization not creating mutable containers 【发布时间】:2012-03-28 17:30:38 【问题描述】:

代码如下:

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

如您所见,我两次调用完全相同的方法来解析 JSON,一次在 JSON 中包含一个空列表,然后是一个包含对象的列表。结果如下:

Is mutable? 0
Is mutable? 1 

问题在于 NSJSONSerialization 似乎没有遵循为空列表创建可变容器的选项。对我来说似乎是一个错误,但也许我只是误解了一些事情。

有什么想法吗?

【问题讨论】:

你确定它们不会以 NSMutableDictionary 的形式出现吗? 在调试控制台中运行po [[listOfObjects class] superclass] 的输出是什么? 我可以在 Mac OS X 10.7 上确认这个问题——似乎只有空数组受到影响。它似乎在 10.8 中修复。 【参考方案1】:

这正如预期的那样工作:

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

这是控制台输出:

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

编辑

请注意,如果我们在上面的代码中添加以下行...

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

...我们在控制台上得到以下输出:

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

...以防万一不清楚__NSArrayMNSMutableArray 的私有子类,从而证明OP 的代码确实按预期工作(除了他的NSLog 声明)。

编辑

哦,对了,下面这行代码……

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

...导致以下控制台输出:

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

编辑(响应更改的问题)

有趣...看起来像一个错误。给定以下代码:

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

这是控制台输出:

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

因此,以这种方式创建的空数组和字典似乎没有按预期运行。

【讨论】:

[expenses class] 仍然是一个 NSArray,而不是一个可变数组,不是吗? 不。首先,您不能将对象添加到NSArray 的实例中。其次,如果查看控制台输出,数组实际上是__NSArrayM 的一个实例,它是NSMutableArray 的私有子类。 嗨,感谢您到目前为止的回复。虽然我已经更新了问题,但我认为我没有正确解释问题.. 你能再检查一下吗?【参考方案2】:

这是我解决此问题的方法:

#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

所以我打电话:

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

而不是通常的:

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

【讨论】:

【参考方案3】:

其他人也认为这是一个错误,请参阅

    https://github.com/couchbaselabs/TouchDB-ios/issues/44 或 例如https://github.com/johnlabarge/jlbiosutils/blob/master/jlbiosutils/DynamicProperties.m。

在后一种情况下,您也可以看到空字典的完整解决方法(请参阅DynamicGetter(...) 方法)。

【讨论】:

Jens Alfke 认为是错误的任何事情对我来说都是错误。感谢您的链接,我最终以几乎相同的方式解决了这个问题。【参考方案4】:

这是我的工作:

BOOL needsWorkaround = YES;
if (needsWorkaround)

    NSMutableDictionary* appState2 = 
        (__bridge_transfer NSMutableDictionary*) 
        CFPropertyListCreateDeepCopy (
            kCFAllocatorDefault, (__bridge void*)appState,
            kCFPropertyListMutableContainersAndLeaves
        );
    appState = appState2;

【讨论】:

以上是关于NSJSONSerialization 不创建可变容器的主要内容,如果未能解决你的问题,请参考以下文章

NSJSONSerialization 不更新数据

NSJSONSerialization 不解析 NSManagedObject

NSJSONSerialization 不工作,NSUTF8StringEncoding 工作

来自 nsmanagedObject 的 NSJSONSerialization 表示键不符合键值编码

NSJSONSerialization Json 嵌套子

NSJSONSerialization 包含正斜杠的字符串序列化 / 和 HTML 被错误地转义