我已经安装了ActiveResource来使用json中的freebase api,它应该可以正常工作,除了json freebase返回导致ActiveResource爆炸。
NoMethodError: undefined method `collect!' for #<Hash:0x007fd674831dd0>
如何定义自定义json解析器来修复错误?
class Freebase < ActiveResource::Base
self.site = "https://www.googleapis.com/"
self.format = :json
def self.search(word)
self.find(:all, :from => "/freebase/v1/search/", :params => { :query => word })
end
#https://www.googleapis.com/freebase/v1/search?query=nirvana
#Freebase.get('search', :query => 'nirvana')
end
Json被退回:
https://www.googleapis.com/freebase/v1/search?query=nirvana
{"status":"200 OK","result":[{"mid":"/m/05b3c","name":"Nirvana","notable":{"name":"Belief","id":"/religion/belief"},"lang":"en","score":67.540009},{"mid":"/m/0b1zz","name":"Nirvana","notable":{"name":"Musical Artist","id":"/music/artist"},"lang":"en","score":64.311432},{"mid":"/m/092bf5","name":"Buddhism","notable":{"name":"Religion","id":"/religion/religion"},"lang":"en","score":33.647118},{"mid":"/m/02_6qh","name":"Nirvana","notable":{"name":"Film","id":"/film/film"},"lang":"en","score":30.068491},{"mid":"/m/01h89tx","name":"Nirvana","notable":{"name":"Musical Album","id":"/music/album"},"lang":"en","score":27.799274},{"mid":"/m/01rn9fm","name":"Nirvana","notable":{"name":"Musical Group","id":"/music/musical_group"},"lang":"en","score":27.445602},{"mid":"/m/015k7","name":"Gautama Buddha","notable":{"name":"Deity","id":"/religion/deity"},"lang":"en","score":24.129679},{"mid":"/m/01rkx5","name":"Mahayana Mahaparinirvana Sutra","lang":"en","score":22.359026},{"mid":"/m/03d7q7v","name":"Nirvana","lang":"en","score":21.034473},{"mid":"/m/055ym7w","name":"Nirvana bootleg recordings","notable":{"name":"Musical Album","id":"/music/album"},"lang":"en","score":19.241596},{"mid":"/m/0122_j","name":"Nevermind","notable":{"name":"Musical Album","id":"/music/album"},"lang":"en","score":18.366383},{"mid":"/m/04n7mt","name":"Nirvana fallacy","lang":"en","score":17.212397},{"mid":"/m/0484q","name":"Kurt Cobain","notable":{"name":"Musician","id":"/m/09jwl"},"lang":"en","score":16.594929},{"mid":"/m/027_k8j","name":"Nirvana","lang":"en","score":16.336584},{"mid":"/m/0285c","name":"Dave Grohl","notable":{"name":"Musician","id":"/m/09jwl"},"lang":"en","score":16.115103},{"mid":"/m/068shv","name":"Smells Like Nirvana","notable":{"name":"Musical Album","id":"/music/album"},"lang":"en","score":15.350652},{"mid":"/m/01kq85c","name":"Manic Nirvana","notable":{"name":"Musical Album","id":"/music/album"},"lang":"en","score":15.275189},{"mid":"/m/0437sc","name":"Lithium","notable":{"name":"Composition","id":"/music/song"},"lang":"en","score":14.637386},{"mid":"/m/055kh1","name":"Mechanus","lang":"en","score":14.621847},{"mid":"/m/01f1vf","name":"Lucifer","notable":{"name":"Fictional Character","id":"/fictional_universe/fictional_character"},"lang":"en","score":13.504528}],"cursor":20,"cost":11,"hits":3104}
使用Rails 3.2.1
更新:
发现此问题,但仍不确定如何克服它。 https://github.com/rails/rails/issues/2318
答案 0 :(得分:1)
问题是返回的JSON没有像ActiveResource期望的那样形成。 ARes不期望返回所有元数据,只期待响应的results
部分。
说实话,你要回来了:
{"status":"200 OK","result":[{"mid":"/m/05b3c","name":"Nirvana","notable":{"name" ...
但是ARes希望:
[{"mid":"/m/05b3c","name":"Nirvana","notable":{"name"...
我能想到的最简单(也可能最脏)的解决方案是覆盖模型中的ActiveResource :: Base#find_every私有方法,如下所示:
class Freebase < ActiveResource::Base
self.site = "https://www.googleapis.com/"
self.format = :json
def self.search(word)
self.find(:all, :from => "/freebase/v1/search/", :params => { :query => word })
end
private
def self.find_every(options)
begin
case from = options[:from]
when Symbol
instantiate_collection(get(from, options[:params]))
when String
path = "#{from}#{query_string(options[:params])}"
instantiate_collection(format.decode(connection.get(path, headers).body['result']) || [])
else
prefix_options, query_options = split_options(options[:params])
path = collection_path(prefix_options, query_options)
instantiate_collection( (format.decode(connection.get(path, headers).body['result']) || []), prefix_options )
end
rescue ActiveResource::ResourceNotFound
# Swallowing ResourceNotFound exceptions and return nil - as per
# ActiveRecord.
nil
end
end
end
我所做的唯一更改是.body
方法调用现在是.body['result']
。通过这个添加,#instantiate_collection
现在将收到一个预期的数组而不是哈希。
最终,虽然这会导致错误消失,但我不知道这会解决您的所有问题,因为您可能需要使用此方法丢失的一些数据。我的建议是放弃ActiveResource,如果你可以使用像RestClient这样的东西并围绕它建立你的模型。这种方法的另一个原因是它们现在的语义被打破了。你没有收回Freebases的集合。您正在使用Freebase API获取搜索结果。
答案 1 :(得分:0)
我遵循了Derek的建议并放弃了ActiveResource ......
对于其他任何人我最终使用法拉第和Faraday_Middleware
class Freebase
def self.search(id)
connection = Faraday.new 'https://www.googleapis.com/freebase/v1' do |conn|
conn.adapter Faraday.default_adapter
conn.use FaradayMiddleware::ParseJson
#conn.use Faraday::Response::Mashify
end
response = connection.get do |req|
req.url('search', :query => id, :limit => 10)#, :filter => '(any namespace:/wikipedia/en_id namespace:/authority/imdb/title)')# #:filter => '(any namespace:/wikipedia/en_id namespace:/authority/imdb/title namespace:/authority/netflix/movie)' , :with => 'commons'
end
end
据说你可以使用几个内置的模块来从返回的哈希中创建rails样式方法,但我还没有想到那个部分。 ActiveResource似乎只擅长其他完全模仿它的Rails应用程序或RESTFUL路径。