1

我正在使用以下 JSON:http ://www.kb.dk/tekst/mobil/aabningstider_en.json 当我尝试通过键“位置”解析它时:

//    get response in the form of a utf-8 encoded json string
NSString *jsonString = [[[NSString alloc] initWithData:responseData encoding:NSUTF8StringEncoding] stringByTrimmingCharactersInSet:[NSCharacterSet newlineCharacterSet]];

//    get most parent node from json string
NSDictionary *json = [jsonString JSONValue];

//    get key-path from jason up to the point of json object
NSDictionary *locations = [json objectForKey:@"location"];

NSLog( @"%@", locations );

//    iterate through all of the location objects in the json
for (NSDictionary *loc in locations )
{

    //        pull library name from the json object
    NSString *name = [loc valueForKey:@"name"];
    //        add library data table arrays respectively
    [ libraryNames addObject: ( ( name == nil | name.length > 0 ) ? name : @"UnNamed" ) ];

}

当我通过 NSLog 打印对象位置时:

{
address = "Universitetsparken 4, 3. etage, 2100 K\U00f8benhavn \U00d8";
desc = "";
lastUpdated = "";
latlng = "55.703124,12.559596";
link = "http://www.farma.ku.dk/index.php?id=3742";
name = "Faculty of Pharmaceutical Sciences Library";
parts =     {
    part =         {
        hour =             {
            day = "5.June Constitution Day (Denmark)";
            open = Closed;
        };
        hours =             {
            hour =                 {
                day = Friday;
                open = "10-16";
            };
        };
        name = main;
    };
};
}

这只是“位置”键的最后一个值。难道我做错了什么?我尝试通过http://jsonlint.com/验证 JSON ,但是当我输入上面的 JSON URL 时,它说“有效” - 仍然只显示最后一个“位置”键”,但是如果我复制-粘贴它,它不会验证 JSON,并且必须通过从字符串中删除换行来修复。

此外,当我尝试解析 JSON 并获取“名称”字段时,出现以下异常:

2012-05-08 15:37:04.941 iPhone App Tabbed[563:f803] *** Terminating app due to uncaught         exception 'NSUnknownKeyException', reason: '[<__NSCFString 0x68bfe70> valueForUndefinedKey:]:     this class is not key value coding-compliant for the key name.'
*** First throw call stack:
(0x13dc052 0x156dd0a 0x13dbf11 0x9d2f0e 0x941841 0x940ca9 0x4593 0xf964e 0x114b89 0x1149bd     0x112f8a 0x112e2f 0x1148f4 0x13ddec9 0x365c2 0x3655a 0x25b569 0x13ddec9 0x365c2 0x3655a     0xdbb76 0xdc03f 0xdbbab 0x25dd1f 0x13ddec9 0x365c2 0x3655a 0xdbb76 0xdc03f 0xdb2fe 0x5ba30     0x5bc56 0x42384 0x35aa9 0x12c6fa9 0x13b01c5 0x1315022 0x131390a 0x1312db4 0x1312ccb 0x12c5879     0x12c593e 0x33a9b 0x281d 0x2785)
terminate called throwing an exception(lldb) 

如果“位置”标签是一个用方括号 ([]) 括起来的数组对象,那会更有意义,但是现在它只是一系列普通的键值对......遗憾的是,这是我必须使用的 JSON .

请帮助并非常感谢!:)

真诚的,彼得。

4

3 回答 3

2

您必须使用的 JSON 可能是有效的,但它没有多大意义。它有一本大字典,其中的location键重复了很多次。大多数 JSON 解析器将简单地返回重复键的最后一个值。如果您可以更改结构以使用数组代替,那将是最好的,但如果您不能,仍然有希望。您可以读取流并将location键中的值从其中出来时填充到数组中。这就是你这样做的方式:

@interface BadJsonHelper : NSObject
@property(strong) NSMutableArray *accumulator;
@end    
@implementation BadJsonHelper
- (void)parser:(SBJsonStreamParser *)parser foundArray:(NSArray *)array {
    // void
}
- (void)parser:(SBJsonStreamParser *)parser foundObject:(NSDictionary *)dict {
    [accumulator addObject:dict];
}
@end

您可以将那个小助手类放在文件顶部,在您正在工作的类的@implementation 部分之外。(@interface 和 @implementation 不需要在不同的文件中。)

在您的代码中,您可以像这样使用它:

BadJsonHelper *helper = [[BadJsonHelper alloc] init];
helper.accumulator = [NSMutableArray array];

SBJsonStreamParserAdapter *adapter = [[SBJsonStreamParserAdapter new] init];
adapter.delegate = helper;
adapter.levelsToSkip = 1;

SBJsonStreamParser *parser = [[SBJsonStreamParser alloc] init];
parser.delegate = adapter;

switch ([parser parse: responseData]) {
    case SBJsonStreamParserComplete: 
        NSLog(@"%@", helper.accumulator);
        break;
    case SBJsonStreamParserWaitingForData:
        NSLog(@"Didn't get all the JSON yet...");
        break;
    case SBJsonStreamParserError:
        NSLog(@"Error: %@", parser.error);
        break;
}

此示例最初改编自以下测试: https ://github.com/stig/json-framework/blob/master/Tests/StreamParserIntegrationTest.m

更新:我创建了一个功能齐全的示例项目,它异步加载 JSON 并解析它。这可以从github获得。

于 2012-05-10T22:36:23.070 回答
1

JSON 是有效的,但是关于项目数组的定义存在一个基本问题。

locationsJSON 不是定义使用括号的数组,而是location一遍又一遍地重新定义相同的键/值对。换句话说,JSON 最初说 location 的值是名为“The Black Diamond”的集合,但在它立即用名为“Faculty Library of Humanities”的集合重新定义它,依此类推,直到最后一个位置 Faculty of Pharmaceutical Sciences Library ”。

parts和也是如此hours

如果您无法修复 JSON 的结果并且您确实需要使其正常工作,您可能需要修改 JSON 删除“位置”键并正确添加括号。

编辑

或者,您可以使用NSScanner并手动处理 JSON 结果。有点骇人听闻,但只要 JSON 格式没有显着变化,它就可以工作。

编辑

这段代码应该可以完成工作......

NSString *jsonString = [[[NSString alloc] initWithData:responseData encoding:NSUTF8StringEncoding] stringByTrimmingCharactersInSet:[NSCharacterSet newlineCharacterSet]];

int indx = 1;
for (;;)
{
    NSRange locationRange = [jsonString rangeOfString:@"\"location\":"];

    if (locationRange.location == NSNotFound) break;

    jsonString = [jsonString stringByReplacingCharactersInRange:locationRange
                                                     withString:[NSString stringWithFormat:@"\"location%d\":", indx++]];
} 
于 2012-05-08T14:03:27.843 回答
1
NSDictionary *locations = [json objectForKey:@"location"];

可以看到,SBJson 解析 JSON 的结果是一个NSDictionary. 字典包含键/值对,键是对的唯一标识符。

您需要处理的 JSON 数据是有效的,但不是一个好的数据。根据RFC 4627 - 2.2

对象结构表示为一对围绕零个或多个名称/值对(或成员)的花括号。名称是一个字符串。每个名称后面都有一个冒号,将名称与值分开。单个逗号将值与后面的名称分开。 对象中的名称应该是唯一的。

jQuery之类的东西也可以解析JSON,但结果与SBJson相同(最后一个为一个)。请参阅JSON 键是否需要唯一?.

这不是必须的,但仍然不是一个好习惯。如果您能够在服务器端(甚至在接收到它之后在客户端)更改 JSON 数据的结构,而不是按原样解析它,那将会容易得多。

于 2012-05-08T14:09:06.483 回答