迭代JSON对象以创建ruby对象的最佳方法

时间:2017-08-02 09:26:25

标签: ruby-on-rails arrays json ruby

我以JSON对象的形式获得ChromeDriver - WebDriver for Chrome响应,其中一些嵌套数组包含一组联系人详细信息。我想在我的Rails app控制器中创建一个对象,该对象包含响应中的任何信息。我尝试使用像bellow这样的代码来做这件事(注意我使用的宝石允许用点表示法访问对象):

@automatic_profile = AutomaticProfile.new(
  profile_id: @profile.id,
  first_name: @intel.contact_info.full_name,
  email: @profile.email,
  gender: @intel.demographics.gender,
  city: @intel.demographics.location_deduced.city.name,
  skype: @intel.contact_info.chats.select { |slot| slot.client == "skype" }[0].handle),
  organization_1: @intel.organizations[0].name if @intel.organizations,
  # other similar lines for other organizations
  twitter: (@intel.social_profiles.select { |slot| slot.type_name == "Twitter" }[0].url if @intel.social_profiles),
  twitter_followers: (@intel.social_profiles.select { |slot| slot.type_name == "Twitter" }[0].followers.to_i) if @intel.social_profiles,
  twitter_following: (@intel.social_profiles.select { |slot| slot.type_name == "Twitter" }[0].following.to_i if @intel.social_profiles),
  # other similar lines for other social profiles
)

我对此代码有两个问题:

  1. Json对象不会总是拥有填充一些散列键所需的所有信息,从而引发异常,例如,调用不存在的数组中的索引。
  2. 我尝试在每一行添加if语句,如下所示:

    twitter: (@intel.social_profiles.select { |slot| slot.type_name == "Twitter" }[0].url if @intel.social_profiles),
    

    但它并不干,我对括号的使用感到困惑,因此我提出了额外的例外情况。

    1. 为了给我的键设置正确的值,我使用插槽方法找到我要查找的特定数据。这看起来很冗长,也不太实用。
    2. 关于创建一个带有嵌套数组响应的大Json数据的对象,您能否就最佳实践提出建议?并建议我如何解决这个特殊情况?

1 个答案:

答案 0 :(得分:2)

您可以使用.first.try的组合。 (和.dig如果你使用的是ruby 2.3),以避免在访问它们时出现异常。

如果无法找到,

.try将返回nil。例如:

{ a: 2 }.try(:b) # returns nil 

.dig.try类似,但它可以达到多个级别,因此这可能对深度嵌套的级别有用。

[['a'], ['a','b']].dig(0, 1) # first element, then second element - nil
[['a'], ['a','b']].dig(1, 1) # second, then second again - 'b'

{ a: [1, 2] }.dig(:a, 0) # 1
{ a: [1, 2] }.dig(:a, 2) # nil

foo = OpenStruct.new
foo.bar = "foobar"
{ b: foo }.dig(:b, :bar) # 'foobar'

@intel.dig(:contact_info, :full_name)
@intel.dig(:organizations, :first, :name)

对于最后一部分,你也可以用这种方式重构它:

def twitter_profile
  return unless @intel.social_profiles.present?
  @intel.social_profiles.find { |slot| slot.type_name == "Twitter" }
end

twitter: twitter_profile.try(:url),
twitter_followers: twitter_profile.try(:followers).to_i,
twitter_followings: twitter_profile.try(:followings).to_i,

twitter_profile可能是控制器中的私有方法。如果您发现自己开始拥有太多这些内容,则可以使用服务对象来创建配置文件。

class ProfileCreatorService
  def initialize(intel)
    @intel = intel
  end

  def perform
    AutomaticProfile.new(...)
  end

  private 

  def twitter_profile
    return unless @intel.social_profiles.present?
    @intel.social_profiles.find { |slot| slot.type_name == "Twitter" }
  end

  ..
end