循环访问 JSON 对象以创建 ruby 对象的最佳方法



我正在以JSON对象的形式获得FullContact API响应,其中包含一些包含一组联系人详细信息的嵌套数组。我想在我的 Rails 应用程序控制器中创建一个对象,该对象包含响应中的任何信息。我正在尝试使用像波纹管这样的代码来做到这一点(请注意,我使用的 gem 允许使用点符号访问对象(:

@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 对象并不总是具有填充某些哈希键所需的所有信息,因此在调用不存在的数组中的索引时会引发异常。

我尝试在每一行中添加一个 if 语句,如下所示:

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

但它不是 DRY,我对括号的使用感到非常困惑,以至于我提出了其他例外。

  1. 为了为我的键设置正确的值,我正在使用 slot 方法来查找我正在寻找的特定数据。 这似乎也很冗长,没有多少实际意义。

您能否就使用来自嵌套数组响应的大 Json 中的数据创建对象的最佳实践提供建议,并就如何解决这种特殊情况提出建议?

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

如果找不到,.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

最新更新