1

I'm writing an iOS app that uses the last.fm web services to search for albums. The following code converts the returned json to an NSArray:

results = (NSArray *)[[[json valueForKey:@"results"] valueForKey:@"albummatches"] valueForKey:@"album"];

This works fine when a search term returns a result, but the application crashes when no results are returned. This is because the key "album" doesn't exist in the json when no results are found.

How to I get my application to check if the "album" key exists in the json?

EDIT: Sample json returned when there are no results. Search string was 'asdfgasdfg'. Please note the character return under the albummatches key.

2013-03-12 14:08:00.889 MyMusicLibrary[387:1a03] {
results =     {
    "@attr" =         {
        for = asdfgasdfg;
    };
    albummatches = "\n";
    "opensearch:Query" =         {
        "#text" = "";
        role = request;
        searchTerms = asdfgasdfg;
        startPage = 1;
    };
    "opensearch:itemsPerPage" = 50;
    "opensearch:startIndex" = 0;
    "opensearch:totalResults" = 0;
};
}

EDIT 2: Sample json when results are returned:

2013-03-12 14:19:42.769 MyMusicLibrary[880:1a03] {
results =     {
    "@attr" =         {
        for = coldplay;
    };
    albummatches =         {
        album =             (
                            {
                artist = Coldplay;
                id = 1416655;
                image =                     (
                                            {
                        "#text" = "http://userserve-ak.last.fm/serve/34s/85845017.png";
                        size = small;
                    },
                                            {
                        "#text" = "http://userserve-ak.last.fm/serve/64s/85845017.png";
                        size = medium;
                    },
                                            {
                        "#text" = "http://userserve-ak.last.fm/serve/126/85845017.png";
                        size = large;
                    },
                                            {
                        "#text" = "http://userserve-ak.last.fm/serve/300x300/85845017.png";
                        size = extralarge;
                    }
                );
                mbid = "c67be398-9417-4c22-bc13-d6158029ae21";
                name = "A Rush of Blood to the Head";
                streamable = 1;
                url = "http://www.last.fm/music/Coldplay/A+Rush+of+Blood+to+the+Head";
            },
                            {
                artist = Coldplay;
                id = 1984;
                image =                     (
                                            {
                        "#text" = "http://userserve-ak.last.fm/serve/34s/87203265.png";
                        size = small;
                    },
                                            {
                        "#text" = "http://userserve-ak.last.fm/serve/64s/87203265.png";
                        size = medium;
                    },
                                            {
                        "#text" = "http://userserve-ak.last.fm/serve/126/87203265.png";
                        size = large;
                    },
                                            {
                        "#text" = "http://userserve-ak.last.fm/serve/300x300/87203265.png";
                        size = extralarge;
                    }
                );
                mbid = "8e602038-c0f2-3c2d-9068-a1a3daca493d";
                name = Parachutes;
                streamable = 1;
                url = "http://www.last.fm/music/Coldplay/Parachutes";
            }
        );
    };
    "opensearch:Query" =         {
        "#text" = "";
        role = request;
        searchTerms = coldplay;
        startPage = 1;
    };
    "opensearch:itemsPerPage" = 2;
    "opensearch:startIndex" = 0;
    "opensearch:totalResults" = 375;
};
}
Cheese1223
  • 107
  • 1
  • 11
  • You assume results key always exists. But if its not than you invoke a method on a nil object – giorashc Mar 11 '13 at 16:06
  • Or that results is always an array, and trying to send message to something that is NOT an array. – Mike D Mar 11 '13 at 16:07
  • Can you provide and NSLog of JSON where it works? – Paul.s Mar 11 '13 at 16:15
  • how do you normally check if a _value_ exists for a _key_ in an `NSDictionary`? the procedure would be the pretty same one... if the _value_ is not existing for a _key_, then the _value_ is `nil`. – holex Mar 12 '13 at 14:42
  • let me correct myself... `if ([[[json valueForKey:@"results"] valueForKey:@"albummatches"] isKindOfClass:[NSDictionary class]]) { / * albums existing */ } else { /* album not existing */ }`, it could be useful for your case. – holex Mar 12 '13 at 14:45

3 Answers3

1

There are two ways you could go about this

  1. Assuming that the "opensearch:totalResults" is in fact the correct results count then you could use this value to determine if you have results or not and then use

    NSArray *results = nil;
    
    if ([[json valueForKeyPath:@"results.opensearch:totalResults"] intValue] > 0) {
      results = [json valueForKeyPath:@"results.albummatches.album"];
    }
    
  2. Check whether you have a string or an array before processing it

    id albummatches = [json valueForKeyPath:@"results.albummatches"];
    
    // is this a dictionary or a string?
    // You can either ask if it implements the interface or ask what class it is. I prefer the first
    BOOL isDictionary = [albummatches respondsToSelector:@selector(objectForKey:)];
    
    // or
    BOOL isDictionary = [albummatches isKindOfClass:[NSDictionary class]];
    
    if (isDictionary) {
      results = [albummatches objectForKey:@"album"];
    }
    
Paul.s
  • 38,494
  • 5
  • 70
  • 88
0

Check:

if([[json[@"result"][@"albummatches"] allKeys] containsObject:@"album"]==YES){

    //do your work here
}
Anoop Vaidya
  • 46,283
  • 15
  • 111
  • 140
0

Someone posted a really good answer earlier, but it was then removed. It helped a lot with this though, and I modified it slightly to get the code below, which now works.

            NSDictionary *resultsDict = [json valueForKey:@"results"];
        if (resultsDict != nil) {
            //Checks to see if any results are returned
            NSDictionary *albumMatchesDict = [resultsDict valueForKey:@"albummatches"];
            NSString *albumMatchesString = [resultsDict valueForKey:@"albummatches"];
            NSCharacterSet *set = [NSCharacterSet whitespaceCharacterSet];
            albumMatchesString = [[albumMatchesString description] stringByTrimmingCharactersInSet:[NSCharacterSet newlineCharacterSet]];

            if ([[albumMatchesString stringByTrimmingCharactersInSet:set] length] != 0) {
                results = (NSArray *) [albumMatchesDict valueForKey:@"album"];
            }
        }
Cheese1223
  • 107
  • 1
  • 11
  • This is a really bad implementation. Can you provide the actual JSON and someone will be able to show you how to do it properly. There is a lot of unnecessary code above to access the dictionaries and then check the result followed by doing a manipulation on the string representation of an array/dictionary this will almost definitely be a pain point later on especially if you don't take the time to understand the bad points about this code and end up reimplementing them everywhere – Paul.s Mar 12 '13 at 09:16
  • The reason I did it like that was because the returned json when there are no results contains an annoying character return under the albummatches key. I'll post the json where there are no results returned in my question above. – Cheese1223 Mar 12 '13 at 14:11
  • Can you also provide a short snippet when there are albums? – Paul.s Mar 12 '13 at 14:14
  • Okay will do I'll post it above again. Thanks for taking a look at this. :) – Cheese1223 Mar 12 '13 at 14:16