从/向JSON API客观化Ruby Hashes

我刚刚发布了一个ruby gem来使用一些基于HTTP API的JSON:

https://github.com/solyaris/blomming_api

我的naif ruby​​代码只是将API端点(json_data)返回的复杂/嵌套JSON数据结构转换为ruby Hashes(hash_data),以一对一的扁平转换(JSON到ruby散列,反之亦然)。 达的很好,但……

我想编程接口更“高级”。 也许为每个端点设置类资源,但我对智能实现感到困惑。

让我用抽象代码解释一下。

假设我有一个API接收的复杂/嵌套JSON,通常是一个哈希数组,递归嵌套如下(想象示例):

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 

我读了有趣的post: http : //pullmonkey.com/2008/01/06/convert-a-ruby-hash-into-a-class-object/ http://www.goodercode.com/wp/convert-your -hash密钥至对象的属性function于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对我的范围来说可能很棒,但问题是ARes对完整REST API有一点太“严格”的假设……在我的情况下,服务器API并不像ARes期望的那样完全是RESTfull …总而言之我会做了很多工作来inheritance/修改ARes行为,目前我放弃了使用ActiveResource的想法

问题:

  1. 有人可以帮我实现上面代码的save()方法(我用递归方法真的很糟糕…… :-()?
  2. 是否存在一些上述草绘的hash_to_object()和object_to_hash()翻译的gem?

  3. 您如何看待JSON上的“任意”哈希“自动”客观化对http API的影响? 我的意思是:我认为我不需要客户端静态线数据结构的优秀专业人员,允许灵活的服务器端变化。 但另一方面,做这种自动客观化,可能会产生一些副作用,以允许安全问题……如恶意JSON注入(可能是未通信的通信网……)

你怎么看待这一切? 欢迎任何建议! 对不起我的长篇文章和我的ruby语言元编程azards 🙂

乔治

更新2:我仍然有兴趣阅读关于问题点3的意见:优点/缺点为每个收到的JSON优点/缺点创建资源类来创建静态(抢占属性)/自动匹配/动态嵌套对象

更新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 

此function由少数gem提供。 其中最着名的是Hashie ,特别是Hashie::Mash

Mash是一个扩展的Hash,它提供了简单的伪对象function,可以从哈希构建并轻松扩展。 它旨在用于RESTful API库,以提供对JSON和XML解析哈希的简单对象访问。

Mash还支持多级对象。

根据您的需求和嵌套级别,您可以使用OpenStruct

我正在使用一个简单的测试存根。 Hashie本来会运作良好,但它是一个比我需要的更大的工具(并增加了依赖性)。