简体   繁体   English

为什么SBJson JSON解析只获得最后一个感兴趣的键?

[英]Why does SBJson JSON parsing only get the last key of interest?

I am using the following JSON: http://www.kb.dk/tekst/mobil/aabningstider_en.json When I try to parse it by the key "location" as such: 我使用以下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" ) ];

}

When I print the the object locations via NSLog: 当我通过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;
    };
};
}

Which is only the last value for the "location" keys. 这只是“位置”键的最后一个值。 Am I doing something wrong? 难道我做错了什么? I tried validating the JSON via http://jsonlint.com/ , however when I'd put in the JSON URL as above, it said "valid" - still only the last "locations" key was shown", however if I copy-paste it, it will not validate the JSON, and has to be fixed by removing new-lines from the string. 我尝试通过http://jsonlint.com/验证JSON,但是当我按上述方式输入JSON URL时,它说“有效” - 仍然只显示最后一个“位置”键,但是如果我复制的话-paste它,它不会验证JSON,必须通过从字符串中删除换行来修复。

Also, when i try to parse the JSON and get the "name" fields, I get the following exception: 此外,当我尝试解析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) 

It would make more sense if the "locations" tag was an array object enclosed by square brackets ([]), however right now it's only an sequence of normal key-value pairs... Sadly, that's the JSON I have to work with. 如果“locations”标签是由方括号([])括起来的数组对象会更有意义,但是现在它只是一系列正常的键值对...可悲的是,这是我必须使用的JSON 。

Please help and thanks a great deal! 请帮忙,非常感谢! :) :)

Sincerely, Piotr. 真诚的,彼得。

The JSON you've got to work with may be valid, but it doesn't make much sense. 你必须使用的JSON可能是有效的,但它没有多大意义。 It has one big dictionary with the location key repeated many times. 它有一个大字典, location键重复多次。 Most JSON parser will simply return the last value for the repeated key. 大多数JSON解析器只返回重复键的最后一个值。 It would be best if you could change the structure to use an array instead, but if you cannot there's still hope. 最好是你可以改变结构而不是使用数组,但如果你不能,那么仍然有希望。 You can read the stream and stuff the values from the location keys into an array as they come out of it. 您可以读取流并将location键中的值填充到它们出来的数组中。 This is how you'd do that: 这就是你如何做到的:

@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

You can drop that little helper class at the top of your file, outside the @implementation section of the class where you're doing your work. 您可以将该小助手类放在文件顶部,在您正在进行工作的类的@implementation部分之外。 (There's no need for the @interface and @implementation being in different files.) (@interface和@implementation不需要在不同的文件中。)

In your code, you would use it like this: 在你的代码中,你会像这样使用它:

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

This example was originally adapted from the following test: https://github.com/stig/json-framework/blob/master/Tests/StreamParserIntegrationTest.m 此示例最初改编自以下测试: https//github.com/stig/json-framework/blob/master/Tests/StreamParserIntegrationTest.m

Update: I created a fully functional example project that loads the JSON asynchronously and parses it. 更新:我创建了一个功能齐全的示例项目,它异步加载JSON并解析它。 This is available from github . 这可以从github获得

The JSON is valid, however there is a basic problem regarding the definition of the array of items. JSON是有效的,但是关于项目数组的定义存在基本问题。

Instead of defining an array of locations using brackets, the JSON redefines the same location key/value pair over and over again. JSON不是使用括号定义locations数组,而是一遍又一遍地重新定义相同的location键/值对。 In other words JSON initially says the value of location is the collection with name "The Black Diamond", but immediately after it redefines it with the collection with name "Faculty Library of Humanities" and so on till the last location Faculty of Pharmaceutical Sciences Library". 换句话说,JSON最初说位置的价值是名为“The Black Diamond”的集合,但是在它用名为“Faculty Human of Humanities”的集合重新定义它之后立即等到最后位置药物科学图书馆”。

The same is true for parts and hours . partshours也是如此。

If you can't fix the result of the JSON and you really need to get it working you may want to modify the JSON removing the "location" keys and adding brackets properly. 如果你无法修复JSON的结果并且你真的需要让它工作,你可能想要修改JSON删除“位置”键并正确添加括号。

Edit 编辑

Alternatively you may use an NSScanner and process the JSON result manually. 或者,您可以使用NSScanner并手动处理JSON结果。 Kinda hacky but it will work as long as the JSON format doesn't change significantly. 有点hacky但只要JSON格式没有显着变化它就会起作用。

Edit 编辑

This snipped of code should do the work... 这段代码应该完成工作......

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++]];
} 
NSDictionary *locations = [json objectForKey:@"location"];

As you can see, the result of JSON parsing by SBJson is a NSDictionary . 如您所见,SBJson的JSON解析结果是NSDictionary A dictionary contains key/value pairs, and the keys are unique identifiers for the pairs. 字典包含键/值对,键是对的唯一标识符。

The JSON data you need to handle is valid but not a good one. 您需要处理的JSON数据有效但不是很好。 Per RFC 4627 - 2.2 : 根据RFC 4627 - 2.2

An object structure is represented as a pair of curly brackets surrounding zero or more name/value pairs (or members). 对象结构表示为围绕零个或多个名称/值对(或成员)的一对花括号。 A name is a string. 名称是一个字符串。 A single colon comes after each name, separating the name from the value. 每个名称后面都有一个冒号,将名称与值分开。 A single comma separates a value from a following name. 单个逗号将值与以下名称分隔开。 The names within an object SHOULD be unique. 对象中的名称应该是唯一的。

Things like jQuery can parse the JSON also, but the result is the same as SBJson (the last one as the one). 像jQuery这样的东西也可以解析JSON,但结果与SBJson(最后一个作为一个)相同。 See Do JSON keys need to be unique? 请参阅Do JSON密钥需要是唯一的吗? .

It is not a MUST, but it's still not a good practice. 它不是必须的,但它仍然不是一个好习惯。 It would be much easier if you are able to change the structure of the JSON data on the server side (or even on the client side after receiving it) rather than parsing it as is. 如果您能够在服务器端(或者甚至在接收它之后在客户端端)更改JSON数据的结构,而不是按原样解析它将会容易得多。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM