NSDictionary objectForKey return value

29,494

Solution 1

I am not familiar with JSON or the json-framework, but clearly objectForKey cannot be used to access the X's since they all have the same key.

If you know that objectForKey:@"OBJECTS" will return either an NSDictionary (single element) or an NSArray of NSDictionarys (multiple X elements), then you could do something like:

if ( ![results isKindOfClass:[NSArray class]] ) {
    results =[NSArray arrayWithObject:results];
}

That will get you a consistent result, assuming you understand exactly how the json-framework will behave. It will be somewhat fragile, if the elements ever return an array of entries instead of an NSDitionary then it will all fall apart.

There may be a configuration setting for the json-framework that lets you control how it behaves in this case and that would be preferable.

Solution 2

NSDictionary *results = [[dict objectForKey:@"OBJECTS"] objectForKey:@"X"];

and

NSDictionary *results = [dict valueForKeyPath:@"OBJECTS.X"];

The above two are exactly same. The first operation is a little costlier operation than the second one.

Share:
29,494
Joe D'Andrea
Author by

Joe D'Andrea

I am a Cloud Architect and Customer/Contributor/Partner Advocate at Red Hat. Recent milestones include fifteen years at AT&T, most recently in Cloud Strategy, Architecture, and Innovation at AT&T Labs Research, and an additional eight years as an enterprise search consultant and top ten-charting native mobile app developer.

Updated on March 07, 2020

Comments

  • Joe D'Andrea
    Joe D'Andrea about 4 years

    I'm using json-framework to create a NSDictionary out of a JSON response. That much works wonderfully.

    Now, within this JSON payload are one or more objects - let's call them X. Sort of like this in XML:

    <OBJECTS>
      <X>
        ...
      </x>
      <X>
        ...
      </X>
      <X>
        ...
      </X>
    </OBJECTS>
    

    When I look in the aforementioned NSDictionary object for all Xs, like so:

    NSDictionary *results = [[dict objectForKey:@"OBJECTS"] objectForKey:@"X"];
    

    Or even:

    NSDictionary *results = [dict valueForKeyPath:@"OBJECTS.X"];
    

    I get, according to gdb, a NSCFArray of NSDictionary objects. (Yes, I smell something funny here too, but more on this in a moment.)

    When there is only one object named X, I get back an honest-to-goodness NSDictionary.

    So ... what should I do to make this behave consistently, no matter how many Xs there are?

    At first glance, I'd think I just change results to be NSArray *, but what happens when I want to fast enumerate over the results? Right now I do this:

    for (NSDictionary *result in results)
    

    In the NSCFArray case (multiple Xs), I get back an individual NSDictionary for each X. In the single X case, I get back the one NSDictionary, except now my perspective is one level too deep. In other words, instead of this (contrived example):

    (gdb) po results
    <NSCFArray 0xd4a940>(
    {
        foo =     {
            bar = "something";
        };
    }
    {
        foo =     {
            bar = "something else";
        };
    }
    )
    

    I get this:

    (gdb) po results
    {
        foo =     {
            bar = "something";
        };
    }
    

    Clues welcome/appreciated! You may even ask if it's necessary to have to break this apart at all, but for now let's suppose this trip is really necessary. (Still, I'm happy to be persuaded otherwise, if someone feels strongly enough about it.)

    Ultimately, at the end of the day, I want to have a NSArray of NSDictionary objects.

  • Joe D'Andrea
    Joe D'Andrea almost 15 years
    Ahh, I didn't realize objectForKey can't be used to access multiple instances of the same key - thanks! (Though it appears to be doing exactly that with the NSCFArray ... or I'm hallucinating ... or it's just undefined behavior and I got lucky.) I just happened across this article, and it handles the case of multiple X's, as it were, so I'm going to give this a try next. If it works, I'll summarize the fix and report back here! iphonedevelopertips.com/cocoa/…
  • Joe D'Andrea
    Joe D'Andrea almost 15 years
    Meanwhile, here's the member function I'm using. Note that it returns a dictionary or array. Barring any library mods, perhaps if I can just test for a dictionary, I can assign it to a one-object array if necessary. json-framework.googlecode.com/svn/trunk/documentation/…
  • Joe D'Andrea
    Joe D'Andrea almost 15 years
    Thanks! In this particular case, "po results" shows the exact same thing for both of those code examples. (Scout's honor!) Since you can't have more than one "X" in a given dictionary, each "X" in this case is it's own NSDictionary object. However ... there may be another reason not to use valueForKeyPath:. :-o belkadan.com/blog/2007/10/…
  • Joe D'Andrea
    Joe D'Andrea almost 15 years
    I was wrong about the multiple instances of the same key. (Of course, I mean, if that's how NSDictionary works, then that's how it works!) It turns out we do indeed have an array of dictionaries, which is exactly what you surmised. :)
  • Joe D'Andrea
    Joe D'Andrea almost 15 years
    While we're at it, it's also worth pointing out Peter's suggestion is the more concise one! We know we're going to get back a dictionary or an array, so in Peter's case, if it's not an array, it's a dictionary and we can just create a one-object array out of it.
  • Joe D'Andrea
    Joe D'Andrea almost 15 years
    I should clarify: If I look at the object for key OBJECTS, I get a single dictionary. If I look in there for the key X, that ends up being array of dictionaries, since there are multiple X's in the original XML/JSON.
  • leviathan
    leviathan almost 11 years
    When using the CLANG compile you may also use objective-c literals dict[@"OBJECTS"] - clang.llvm.org/docs/ObjectiveCLiterals.html
  • Radu Ursache
    Radu Ursache over 7 years
    this answer helped me on other issue! thank you so much