Question

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.

Was it helpful?

Solution

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.

OTHER TIPS

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.

Here's what I ended up doing:

// Get the JSON response and return the result:
NSString *jsonString = [NSString stringWithContentsOfURL:url];
NSDictionary *dict = [jsonString JSONValue];

// If OBJECTS.X gets a dictionary (one value), make an array of one.
id myObject = [dict valueForKeyPath:@"OBJECTS.X"];
if ([myObject isKindOfClass:[NSDictionary class]]) {
    results = [NSArray arrayWithObject:myObject];
} else {
    results = myObject;
}

It's worth pointing out that JSONValue can also return a dictionary or an array. However, I'm looking inside the dictionary post-assignment (OBJECTS.X).

I don't think your two code examples,

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

and

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

are equivalent. Key-Value-Coding has some extra smarts to work with collections, which might be throwing you off here. (An example can be found in the blog post Theocacao: NSArray and KVC by Scott Stevenson.) If you revert back to the former you might get the behaviour you expect.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top