5

我正在开发一个应用程序,我想在其中对服务器进行远程搜索。我希望 RestKit 将检索到的数据保存到数据库中。我首先执行本地搜索(当前有效),然后我想进行远程搜索,然后用新结果更新表格视图。

我有两个问题,1. 我的映射应该是什么样子,2. json 返回一个包含两种不同类型对象的数组。

URL 如下所示:

search.json?search=[search string]

它返回的 JSON 如下所示:

[
  {
    "event": {
      "id": 2,
      [...]
  },
  {
    "news": {
      "id": 16,
      [...]
  }

其中事件和新闻是两种对象。

在我的应用程序中,我有三个模型,Post(抽象实体和超类)NewsPost(Post 的子类)和 Event(Post 的子类)。

我的映射如下所示:

RKManagedObjectMapping* newsMapping = [RKManagedObjectMapping mappingForClass:[NewsPost class] inManagedObjectStore:objectManager.objectStore];   
newsMapping.primaryKeyAttribute = @"newsId";
newsMapping.rootKeyPath = @"news";
[newsMapping mapKeyPath:@"id" toAttribute:@"newsId"];

RKManagedObjectMapping *eventMapping = [RKManagedObjectMapping mappingForClass:[CalendarEvent class] inManagedObjectStore:objectManager.objectStore];
eventMapping.primaryKeyAttribute = @"calendarId";
eventMapping.rootKeyPath = @"calendars";
[eventMapping mapKeyPath:@"id" toAttribute:@"calendarId"];

// These two works. 
[objectManager.mappingProvider setObjectMapping:newsMapping forResourcePathPattern:@"/package_components/1/news"];
[objectManager.mappingProvider setObjectMapping:eventMapping forResourcePathPattern:@"/package_components/1/calendars"];

// I don't know how these should look/work. 
// Since the search word can change
[objectManager.mappingProvider setObjectMapping:eventMapping forResourcePathPattern:@"/package_components/1/search\\.json?search="];
[objectManager.mappingProvider setObjectMapping:newsMapping forResourcePathPattern:@"/package_components/1/search\\.json?search="];

我的搜索代码如下所示(本地搜索有效):

- (void)setUpSearch
{
    if (self.searchField.text != nil) {

        [self.posts removeAllObjects];
        [self.events removeAllObjects];
        [self.news removeAllObjects];

        // Search predicates.
        // Performs local search.
        NSPredicate *contactNamePredicate = [NSPredicate predicateWithFormat:@"contactName contains[cd] %@", self.searchField.text];
        NSPredicate *contactDepartmentPredicate = [NSPredicate predicateWithFormat:@"contactDepartment contains[cd] %@", self.searchField.text];
        [...]

        NSArray *predicatesArray = [NSArray arrayWithObjects:contactNamePredicate, contactDepartmentPredicate, contactEmailPredicate, contactPhonePredicate, linkPredicate, titlePredicate, nil];

        NSPredicate *predicate = [NSCompoundPredicate orPredicateWithSubpredicates:predicatesArray];

        self.posts = [[Post findAllWithPredicate:predicate] mutableCopy];

        if (self.posts.count != 0) {
            self.noResultsLabel.hidden = YES;
            for (int i = 0; i < self.posts.count; i++) {
                Post * post = [self.posts objectAtIndex:i];
                if (post.calendarEvent == YES) {
                    [self.events addObject:post];
                } else {
                    [self.news addObject:post];
                }
            }
        } 

        // reload the table view
        [self.tableView reloadData];

        [self performRemoteSearch];
    }
}

- (void)search
{    
    [self setUpSearch];
    [self hideKeyboard];
    [self performRemoteSearch];
}


- (void)performRemoteSearch
{
    // Should load the objects from JSON    
    // Note that the searchPath can vary depending on search text. 
    NSString *searchPath = [NSString stringWithFormat:@"/package_components/1/search.json?search=%@", self.searchField.text];
    RKObjectManager *objectManager = [RKObjectManager sharedManager];
    [objectManager loadObjectsAtResourcePath:searchPath delegate:self];
}

- (void)objectLoader:(RKObjectLoader*)objectLoader didLoadObjects:(NSArray*)objects
{
    // This never gets called. 

    // Should update my arrays and then update the tableview, but it never gets called. 
    // Instead I get Error Domain=org.restkit.RestKit.ErrorDomain Code=1001 "Could not find an object mapping for keyPath: ''
}

任何关于我应该或可以如何做的提示将不胜感激。

4

2 回答 2

3

我以前没有使用过托管对象,但这里要做的第一件事是通过对象映射和网络请求激活 restkit 日志,这样您就可以检查 restkit 从服务器获取什么以及映射是如何工作的。

//This can be added in your app delegate
RKLogConfigureByName("RestKit/Network", RKLogLevelDebug);
RKLogConfigureByName("RestKit/ObjectMapping", RKLogLevelTrace);

其次,根据您的 JSON 和您的搜索路径发生变化,我认为最好使用映射作为键路径而不是资源路径模式。所以你应该尝试按键映射,就像在这个例子中一样:

RKObjectMapping* articleMapping = [RKObjectMapping mappingForClass:[Article class]];
[articleMapping mapKeyPath:@"title" toAttribute:@"title"];
[articleMapping mapKeyPath:@"body" toAttribute:@"body"];
[articleMapping mapKeyPath:@"author" toAttribute:@"author"];
[articleMapping mapKeyPath:@"publication_date" toAttribute:@"publicationDate"];

[[RKObjectManager sharedManager].mappingProvider setMapping:articleMapping forKeyPath:@"articles"];

然后加载您的数据,例如:

- (void)loadArticles {
    [[RKObjectManager sharedManager] loadObjectsAtResourcePath:@"/articles" delegate:self];
}

另一种方法是按对象映射,因此 RestKit 检测对象的类型并执行映射,然后您向任何路径发出请求。

如果您有任何问题,请发表评论,我可以根据需要改进我的答案。

于 2012-08-14T14:04:05.640 回答
1

我以前从未尝试过用赏金回答问题,让我尝试从最近的一些工作中给出有用的答案=)

1. 我的映射应该是什么样子

从您的代码来看,一切看起来都很好。对象有嵌套吗?您是否需要序列化才能发回服务器?

2. json 返回一个包含两种不同对象的数组。

您的属性是否相同(即事件有标题,事件有日期)没有意外?如果没有,您必须使用dynamic nesting.

如果资源路径(即您的搜索路径)接收到具有不同对象的集合(您的情况),则必须使用dynamic object mapping来加载对象。

由于您可以编辑 JSON 结构,因此利用 RestKit 可以使事情变得更简单。

- 确保 JSON 具有两种不同类型对象的 root_key_path。

从一个旧的实验和一些谷歌搜索来看,如果它们具有正确的 rootKeyPaths,RestKit 可以正确地将 json 输出映射到不同的对象。生成的 JSON 应该具有如下粗略的结构:

{
  "news" : [
    {
      "id" : 1,
      "title" : "Mohawk guy quits"
    },
    {
      "id" : 2,
      "title" : "Obama gets mohawk"
    }
  ],
  "events" : [
    {
      "id" : 1,
      "name" : "testing"
    },
    {
      "id" : 2,
      "name" : "testing again"
    }
  ]
}

我不能确定以上 100% 是正确的。您可以通过让您的 API 仅返回新闻(如果有效)进行试验,然后将事件数据添加到组合中。

- 从服务器加载对象

// Make a NS dictionary and use stringByAppendingQueryParameters

NSDictionary *searchParams = [NSDictionary dictionaryWithKeysAndObjects:
                                @"query",@"myQuery", 
                                @"location",@"1.394168,103.895473",
                                nil];

[[RKObjectManager sharedManager] loadObjectsAtResourcePath:[@"/path/to/resource.json"  stringByAppendingQueryParameters:searchParams] delegate:objectLoaderDelegate];

- 在您的 objectLoader Delegate 中处理“真实”搜索

如果可行,则应将对象映射到您的 Coredata 实体。您可以使用上面发布的方法执行本地搜索NSPredicate

我更喜欢 RestKit 用来loadObjects...从服务器获取数据并映射它的设计模式,其余的处理在本地完成。这种解耦使事情变得更像“应用程序”。您可以使用 NSPredicates 进行其他形式的操作。

- (void)objectLoader:(RKObjectLoader*)objectLoader didLoadObjects:(NSArray*)objects { 
  // Do some processing here on the array of returned objects or cede control to a method that 
  // you've built for the search, like the above method.
}

例如,如果搜索用例是附近的餐馆,那么加载当前纬度/经度范围内的所有餐馆,然后使用 Coredata 按名称执行本地过滤可能是有意义的。你的服务器会让你心动。

让我知道,我会尝试进一步改进答案。

于 2012-08-15T11:40:17.623 回答