Search code examples
iphoneobjective-ciosjsonsbjson

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:

//    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:

{
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.

Also, when i try to parse the JSON and get the "name" fields, I get the following exception:

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.

Please help and thanks a great deal! :)

Sincerely, Piotr.


Solution

  • The JSON is valid, however there is a basic problem regarding the definition of the array of items.

    Instead of defining an array of locations using brackets, the JSON redefines the same location key/value pair over and over again. 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".

    The same is true for parts and hours.

    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.

    Edit

    Alternatively you may use an NSScanner and process the JSON result manually. Kinda hacky but it will work as long as the JSON format doesn't change significantly.

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