2

With the help of mja, I managed to successfully set up simple object mapping using RestKit and Objective-C. Please see my previous question here.

My next step was to attempt to deal with nested JSON in the same way.

My JSON looks like this, with the outer being a CandidatePhrase with some nested 'Votes':

{ "Id":33696,
"Phrase": "phrase",
"BadCount":0,
"Votes":[{"Id":447,"OriginalId":33696,"Votes":2,"Translation":"translation 1"},
{"Id":746,"OriginalId":33696,"Votes":1,"Translation":"translation 2"},
{"Id":747,"OriginalId":33696,"Votes":1,"Translation":"translation 3"}
]}

I created a relationship in my AppDelegate as follows:

[candidatePhraseMapping mapKeyPath:@"votes" toRelationship:@"vote" withMapping:voteMapping];

When I call make my request in my controller, I'm able to deal with the rest of the CandidatePhrase okay, but am not really sure how to map the nested 'Vote' objects into an array so I can use them in a TableView

(pseudo-code something like this...)

// Store the votes in an array
_votes = [[NSArray alloc] initWithObjects:myCandidatePhrase.votes, nil];

Here's my CandidatePhrase Object

@interface CandidatePhrase : NSObject

@property (nonatomic, retain) NSNumber* ident;
@property (nonatomic, retain) NSNumber* badcount;
@property (nonatomic, retain) NSString* phrase;
@property (nonatomic, retain) NSArray* votes;

@end

and my Vote object

@interface Vote : NSObject

@property (nonatomic, retain) NSNumber* ident;
@property (nonatomic, retain) NSNumber* originalId;
@property (nonatomic, retain) NSNumber* votecount;
@property (nonatomic, retain) NSString* translation;

+ (id)voteWithTranslationId:(NSNumber *)ident translation:(NSString *)translation;

@end

Any help would be much appreciated.

EDIT

Below is my mapping code

// Votes Mapping

RKObjectMapping* voteMapping = [RKObjectMapping mappingForClass:[Vote class]];
[voteMapping mapKeyPath:@"Id" toAttribute:@"ident"];
[voteMapping mapKeyPath:@"OriginalId" toAttribute:@"originalId"];
[voteMapping mapKeyPath:@"Votes" toAttribute:@"votecount"];
[voteMapping mapKeyPath:@"Translation" toAttribute:@"translation"];

[[manager mappingProvider] addObjectMapping:voteMapping];

// Candidate Phrase Mapping
RKObjectMapping *candidatePhraseMapping = [RKObjectMapping mappingForClass:[CandidatePhrase class]];

[candidatePhraseMapping mapKeyPath:@"Id" toAttribute:@"ident"];
[candidatePhraseMapping mapKeyPath:@"Phrase" toAttribute:@"phrase"];
[candidatePhraseMapping mapKeyPath:@"BadCount" toAttribute:@"badcount"];

[candidatePhraseMapping mapKeyPath:@"Votes" toRelationship:@"votes" withMapping:voteMapping];

[[manager mappingProvider] addObjectMapping:candidatePhraseMapping];

For clarity also, here's how I'm attempting to access the vote items on the controller

- (void)objectLoader:(RKObjectLoader*)objectLoader didLoadObject:(id)object 
{ 
    CandidatePhrase *myCandidatePhrase = (CandidatePhrase*)object;
    self.candidateText.text = myCandidatePhrase.phrase; <-- works fine

    _votes = [[NSArray alloc] initWithObjects:myCandidatePhrase.votes, nil];
    for (id o2 in _votes) { 
        //Vote *vote = o2; 
        NSLog(@"Item name: %@", o2); <-- sees object but crashes
    }

    NSLog(@"Votes: %@", myCandidatePhrase.votes); 
    _votes = [[NSArray alloc] initWithObjects:myCandidatePhrase.votes, nil];
     [_votesTableView reloadData];
 } 

and my table is binding with

Vote *vote = [_votes objectAtIndex:indexPath.row];
cell.textLabel.text = vote.translation;
Community
  • 1
  • 1
Nick
  • 5,844
  • 11
  • 52
  • 98

1 Answers1

3

You do not need to manually manage the nested NSArray. I believe the problem might be just a simple typo, as you map keyPath "votes", but your json contain "Votes" with capital "V".

[candidatePhraseMapping mapKeyPath:@"Votes" toRelationship:@"votes" withMapping:voteMapping];

If this doesn't help feel free to leave a comment and update your question with voteMapping.

Also, the contents of the didLoadObject can be simplified:

//in .h file
@property (nonatomic, retain) NSArray* votes;

// in implementation
@synthesize votes;

...
- (void)objectLoader:(RKObjectLoader*)objectLoader didLoadObject:(id)object 
{ 
    CandidatePhrase *myCandidatePhrase = (CandidatePhrase*)object;
    self.candidateText.text = myCandidatePhrase.phrase;
    self.votes = myCandidatePhrase.votes;
}
mja
  • 5,056
  • 1
  • 29
  • 40
  • Thanks for this. I updated my code above and am getting the error 'NSUnknownKeyException', reason: '[ valueForUndefinedKey:]: this class is not key value coding-compliant for the key vote.' – Nick Dec 10 '11 at 11:21
  • I've also updated my code for accessing the vote property at the other end, if this helps. – Nick Dec 10 '11 at 11:38
  • Hi, i think this line is the problem... _votes = [[NSArray alloc] initWithObjects:myCandidatePhrase.votes, nil]; the myCandidatePhrase.votes is an array by itself. Do you want to have an array that has just one object - being an array of votes? – mja Dec 10 '11 at 11:42
  • reading your last edit, apparently not. Why not just keep the whole (CandidatePhrase*)object; in a property? What type is the self.candidateText property? – mja Dec 10 '11 at 11:46
  • This is NSString. CandidatePhrase is the outer object, and can have multiple votes associated with it. – Nick Dec 10 '11 at 12:02
  • Apologies. CandidateText is just a label for output – Nick Dec 10 '11 at 12:08
  • Ah i see. Okay then, define a new property of type NSArray (e.g. @property (nonatomic, retain) NSArray* votes; and assign the votes to your new property like this: self.votes = myCandidatePhrase.votes; (keep lines 1&2, add the self.votes assignment and remove the rest of the delegate callback contents) – mja Dec 10 '11 at 12:23
  • Typical. I think I was trying to over-complicate things. This works spot on. Thanks! – Nick Dec 10 '11 at 13:13
  • 1
    You're welcome. I also updated my answer with the hint from my comment. – mja Dec 10 '11 at 13:24