我刚刚发布了一个ruby gem来使用一些JSON over HTTP API:
https://github.com/solyaris/blomming_api
我的naif ruby代码只是将API端点(json_data)返回的复杂/嵌套JSON数据结构转换为ruby哈希(hash_data),在一个平面的一对一事务中(JSON到ruby哈希,反之亦然)。达特很好但是。。
我想一个编程接口更“高层次”。也许为每个端点都初始化一个类Resource,但我对智能实现感到困惑。
让我用一个抽象的代码来解释。
假设我有一个复杂的/嵌套的JSON,由API接收,通常是一个哈希数组,递归嵌套如下(想象的例子):
json_data = '[{
"commute": {
"minutes": 0,
"startTime": "Wed May 06 22:14:12 EDT 2014",
"locations": [
{
"latitude": "40.4220061",
"longitude": "40.4220061"
},
{
"latitude": "40.4989909",
"longitude": "40.48989805"
},
{
"latitude": "40.4111169",
"longitude": "40.42222869"
}
]
}
},
{
"commute": {
"minutes": 2,
"startTime": "Wed May 28 20:14:12 EDT 2014",
"locations": [
{
"latitude": "43.4220063",
"longitude": "43.4220063"
}
]
}
}]'
目前我所做的,当我收到一个类似的JSON表单时,API只是:
# from JSON to hash
hash_data = JSON.load json_data
# and to assign values:
coords = hash_data.first["commute"]["locations"].last
coords["longitude"] = "40.00" # was "40.4111169"
coords["latitude"] = "41.00" # was "40.42222869"
这是好的,但有可怕/混乱的语法。相反,我可能会喜欢这样的东西:
# create object Resource from hash
res = Resource.create( hash_data )
# ... some processing
# assign a "nested" variables: longitude, latitude of object: res
coords = res.first.commute.locations.last
coords.longitude = "40.00" # was "40.4111169"
coords.latitude = "41.00" # was "40.42222869"
# ... some processing
# convert modified object: res into an hash again:
modified_hash = res.save
# and probably at least I'll recover to to JSON:
modified_json = JSON.dump modified_hash
我读了一些有趣的帖子:http://pullmonkey.com/2008/01/06/convert-a-ruby-hash-into-a-class-object/http://www.goodercode.com/wp/convert-your-hash-keys-to-object-properties-in-ruby/
并复制Kerry Wilson的代码,我在下面概述了实现:
class Resource
def self.create (hash)
new ( hash)
end
def initialize ( hash)
hash.to_obj
end
def save
# or to_hash()
# todo! HELP! (see later)
end
end
class ::Hash
# add keys to hash
def to_obj
self.each do |k,v|
v.to_obj if v.kind_of? Hash
v.to_obj if v.kind_of? Array
k=k.gsub(/\.|\s|-|\/|\'/, '_').downcase.to_sym
## create and initialize an instance variable for this key/value pair
self.instance_variable_set("@#{k}", v)
## create the getter that returns the instance variable
self.class.send(:define_method, k, proc{self.instance_variable_get("@#{k}")})
## create the setter that sets the instance variable
self.class.send(:define_method, "#{k}=", proc{|v| self.instance_variable_set("@#{k}", v)})
end
return self
end
end
class ::Array
def to_obj
self.map { |v| v.to_obj }
end
end
#------------------------------------------------------------
顺便说一句,我研究了一点ActiveResource项目(如果我理解得很好,它是Rails的一部分)。对于我的范围来说,战神可能很棒,但问题是ARes对完整的REST API的假设有点过于“严格”。在我的例子中,服务器API并不像战神期望的那样完全是RESTfull的……总而言之,我会做很多工作来子类化/修改战神行为,目前我放弃了使用ActiveResource的想法
问题:
1.有人可以帮助我实现上面代码的save()
方法(我真的不擅长递归方法...:-()?
1.是否存在一些宝石,以上述素描hash_to_object()
和object_to_hash()
翻译?
1.您如何看待通过http API对来自JSON的“任意”散列进行“自动”对象化?我的意思是:我看到了一个伟大的优点,我不需要客户端静态数据结构,允许灵活地适应可能的服务器端变化。但另一方面,做这个自动objectify,有一个可能的缺点的副作用,让安全问题...比如恶意JSON注入(可能是不可信的通信网络……)
你对这一切有什么看法?欢迎任何建议!很抱歉我的长文和我的ruby语言元编程危害:-)
更新2:我仍然有兴趣阅读关于问题3的意见:优点/缺点:为每个收到的JSON创建Resource类优点/缺点:创建静态(抢占式属性)/自动/动态嵌套对象
更新1:回复Simone:谢谢,你是对的,Mash有一个很好的.to_hash()方法:
require 'json'
require 'hashie'
json_data = '{
"commute": {
"minutes": 0,
"startTime": "Wed May 06 22:14:12 EDT 2014",
"locations": [
{
"latitude": "40.4220061",
"longitude": "40.4220061"
},
{
"latitude": "40.4989909",
"longitude": "40.48989805"
},
{
"latitude": "40.4111169",
"longitude": "40.42222869"
}
]
}
}'
# trasforma in hash
hash = JSON.load json_data
puts hash
res = Hashie::Mash.new hash
# assign a "nested" variables: longitude, latitude of object: res
coords = res.commute.locations.last
coords.longitude = "40.00" # was "40.4111169"
coords.latitude = "41.00" # was "40.42222869"
puts; puts "longitude: #{res.commute.locations.last.longitude}"
puts "latitude: #{res.commute.locations.last.latitude}"
modified_hash = res.to_hash
puts; puts modified_hash
3条答案
按热度按时间sauutmhj1#
这个特性是由一些gem提供的。其中最著名的是
Hashie
,特别是Hashie::Mash
类。Mash是一个扩展的Hash,它提供了简单的伪对象功能,可以从哈希构建并轻松扩展。它被设计用于RESTful API库中,以提供对JSON和XML解析散列的简单对象式访问。
Mash
还支持多级对象。des4xlb02#
根据您的需要和嵌套级别,您可以使用
OpenStruct
。我正在使用一个简单的测试存根。
Hashie
本来可以很好地工作,但它是一个比我需要的更大的工具(并增加了依赖性)。toe950273#
最近的ruby版本(至少2.3,可能更早)允许直接解析成OpenStruct。
给定问题中的json_data:
json_data被解析为一个带有访问器的OpenStruct对象,而不需要向项目中添加依赖项。