在Ruby中建模复杂的JSON
我有一段JSON,它是NBA比赛的boxscore,如下例所示。是否有任何策略可以将这些数据转换成一个健全的Ruby模型(可能使用序列化器)?当我从远程API中提取这些数据时,这个JSON中有很多嵌套的数据,例如个人玩家统计、团队统计、团队数据等。最好使用sane Ruby建模工具来处理这个JSON。没关系,但我将尝试将此建模应用到ruby gem中,而不是rails应用程序中 下面是我正在处理的一个JSON blob示例:在Ruby中建模复杂的JSON,ruby,json,rubygems,Ruby,Json,Rubygems,我有一段JSON,它是NBA比赛的boxscore,如下例所示。是否有任何策略可以将这些数据转换成一个健全的Ruby模型(可能使用序列化器)?当我从远程API中提取这些数据时,这个JSON中有很多嵌套的数据,例如个人玩家统计、团队统计、团队数据等。最好使用sane Ruby建模工具来处理这个JSON。没关系,但我将尝试将此建模应用到ruby gem中,而不是rails应用程序中 下面是我正在处理的一个JSON blob示例: [ { "game_length": "2:38",
[
{
"game_length": "2:38",
"location": "Sleep Train Arena",
"game_time": "10:00 PM",
"season_type": "Regular",
"game_id": 19123,
"scores_by_quarter": [
{
"scores": {
"quarter": "1",
"away_team_score": 23,
"home_team_score": 24
}
},
{
"scores": {
"quarter": "2",
"away_team_score": 27,
"home_team_score": 21
}
},
{
"scores": {
"quarter": "3",
"away_team_score": 23,
"home_team_score": 27
}
},
{
"scores": {
"quarter": "4",
"away_team_score": 21,
"home_team_score": 22
}
},
{
"scores": {
"quarter": "OT",
"away_team_score": 10,
"home_team_score": 4
}
}
],
"team_stats": [
{
"team": {
"team_id": 101,
"team_name": "LA Clippers",
"listings": [
{
"listing": {
"freethrows_attempted": 2,
"freethrows_made": 0,
"field_goals_attempted": 5,
"turnovers": 1,
"total_rebounds": 3,
"blocks": 0,
"steals": 1,
"assists": 1,
"field_goals_made": 2,
"offensive_rebounds": 1,
"player_first_name": "Jared",
"three_points_made": 1,
"player_last_name": "Dudley",
"points": 5,
"personal_fouls": 4,
"three_points_attempted": 3,
"minutes_played": 37,
"technical_fouls": 0,
"player_position": "F",
"player_id": 1292,
"started": "Yes",
"seconds_played": 16,
"defensive_rebounds": 2
}
},
{
"listing": {
"freethrows_attempted": 10,
"freethrows_made": 7,
"field_goals_attempted": 15,
"turnovers": 4,
"total_rebounds": 12,
"blocks": 0,
"steals": 0,
"assists": 1,
"field_goals_made": 7,
"offensive_rebounds": 2,
"player_first_name": "Blake",
"three_points_made": 0,
"player_last_name": "Griffin",
"points": 21,
"personal_fouls": 4,
"three_points_attempted": 0,
"minutes_played": 38,
"technical_fouls": 1,
"player_position": "F",
"player_id": 1590,
"started": "Yes",
"seconds_played": 57,
"defensive_rebounds": 10
}
},
{
"listing": {
"freethrows_attempted": 5,
"freethrows_made": 0,
"field_goals_attempted": 9,
"turnovers": 1,
"total_rebounds": 15,
"blocks": 9,
"steals": 0,
"assists": 2,
"field_goals_made": 5,
"offensive_rebounds": 6,
"player_first_name": "DeAndre",
"three_points_made": 0,
"player_last_name": "Jordan",
"points": 10,
"personal_fouls": 1,
"three_points_attempted": 0,
"minutes_played": 40,
"technical_fouls": 0,
"player_position": "C",
"player_id": 1487,
"started": "Yes",
"seconds_played": 14,
"defensive_rebounds": 9
}
},
{
"listing": {
"freethrows_attempted": 4,
"freethrows_made": 4,
"field_goals_attempted": 9,
"turnovers": 1,
"total_rebounds": 1,
"blocks": 0,
"steals": 1,
"assists": 2,
"field_goals_made": 4,
"offensive_rebounds": 0,
"player_first_name": "J.J.",
"three_points_made": 1,
"player_last_name": "Redick",
"points": 13,
"personal_fouls": 2,
"three_points_attempted": 4,
"minutes_played": 16,
"technical_fouls": 0,
"player_position": "G",
"player_id": 1129,
"started": "Yes",
"seconds_played": 59,
"defensive_rebounds": 1
}
},
{
"listing": {
"freethrows_attempted": 4,
"freethrows_made": 4,
"field_goals_attempted": 12,
"turnovers": 1,
"total_rebounds": 1,
"blocks": 0,
"steals": 1,
"assists": 2,
"field_goals_made": 5,
"offensive_rebounds": 0,
"player_first_name": "Darren",
"three_points_made": 1,
"player_last_name": "Collison",
"points": 15,
"personal_fouls": 1,
"three_points_attempted": 2,
"minutes_played": 40,
"technical_fouls": 0,
"player_position": "G",
"player_id": 1578,
"started": "Yes",
"seconds_played": 7,
"defensive_rebounds": 1
}
},
{
"listing": {
"freethrows_attempted": 4,
"freethrows_made": 4,
"field_goals_attempted": 22,
"turnovers": 2,
"total_rebounds": 7,
"blocks": 0,
"steals": 0,
"assists": 11,
"field_goals_made": 12,
"offensive_rebounds": 2,
"player_first_name": "Jamal",
"three_points_made": 3,
"player_last_name": "Crawford",
"points": 31,
"personal_fouls": 1,
"three_points_attempted": 7,
"minutes_played": 37,
"technical_fouls": 0,
"player_position": "G",
"player_id": 299,
"started": "No",
"seconds_played": 29,
"defensive_rebounds": 5
}
},
{
"listing": {
"freethrows_attempted": 4,
"freethrows_made": 3,
"field_goals_attempted": 5,
"turnovers": 2,
"total_rebounds": 5,
"blocks": 0,
"steals": 1,
"assists": 1,
"field_goals_made": 1,
"offensive_rebounds": 1,
"player_first_name": "Antawn",
"three_points_made": 0,
"player_last_name": "Jamison",
"points": 5,
"personal_fouls": 1,
"three_points_attempted": 0,
"minutes_played": 16,
"technical_fouls": 0,
"player_position": "F",
"player_id": 682,
"started": "No",
"seconds_played": 37,
"defensive_rebounds": 4
}
},
{
"listing": {
"freethrows_attempted": 2,
"freethrows_made": 1,
"field_goals_attempted": 1,
"turnovers": 0,
"total_rebounds": 0,
"blocks": 0,
"steals": 0,
"assists": 0,
"field_goals_made": 1,
"offensive_rebounds": 0,
"player_first_name": "Ryan",
"three_points_made": 0,
"player_last_name": "Hollins",
"points": 3,
"personal_fouls": 2,
"three_points_attempted": 0,
"minutes_played": 10,
"technical_fouls": 0,
"player_position": "C",
"player_id": 1198,
"started": "No",
"seconds_played": 11,
"defensive_rebounds": 0
}
},
{
"listing": {
"freethrows_attempted": 2,
"freethrows_made": 1,
"field_goals_attempted": 4,
"turnovers": 0,
"total_rebounds": 1,
"blocks": 0,
"steals": 0,
"assists": 0,
"field_goals_made": 0,
"offensive_rebounds": 1,
"player_first_name": "Reggie",
"three_points_made": 0,
"player_last_name": "Bullock",
"points": 1,
"personal_fouls": 0,
"three_points_attempted": 3,
"minutes_played": 12,
"technical_fouls": 0,
"player_position": "F",
"player_id": 2408,
"started": "No",
"seconds_played": 3,
"defensive_rebounds": 0
}
},
{
"listing": {
"freethrows_attempted": 0,
"freethrows_made": 0,
"field_goals_attempted": 1,
"turnovers": 0,
"total_rebounds": 1,
"blocks": 0,
"steals": 0,
"assists": 0,
"field_goals_made": 0,
"offensive_rebounds": 0,
"player_first_name": "Willie",
"three_points_made": 0,
"player_last_name": "Green",
"points": 0,
"personal_fouls": 0,
"three_points_attempted": 1,
"minutes_played": 15,
"technical_fouls": 0,
"player_position": "G",
"player_id": 634,
"started": "No",
"seconds_played": 7,
"defensive_rebounds": 1
}
},
{
"listing": {
"player_first_name": "Chris",
"reason": "Coach's Decision",
"player_last_name": "Paul",
"status": "Did Not Play",
"player_id": 1011
}
},
{
"listing": {
"player_first_name": "Matt",
"reason": "Coach's Decision",
"player_last_name": "Barnes",
"status": "Did Not Play",
"player_id": 827
}
},
{
"listing": {
"player_first_name": "Byron",
"reason": "Coach's Decision",
"player_last_name": "Mullens",
"status": "Did Not Play",
"player_id": 1568
}
},
{
"listing": {
"player_first_name": "Maalik",
"reason": "Injured",
"player_last_name": "Wayns",
"status": "Did Not Play",
"player_id": 2344
}
}
],
"totals": {
"freethrows_attempted": 37,
"freethrows_made": 24,
"field_goals_attempted": 83,
"total_rebounds": 46,
"points_off_turnovers": 18,
"blocks": 9,
"illegal_defense": 1,
"steals": 4,
"assists": 20,
"field_goals_made": 37,
"turnoveres": 12,
"offensive_rebounds": 13,
"team_turnovers": 12,
"three_points_made": 6,
"team_rebounds": 14,
"shooting_percentages": {
"field_goal_percentage": 0.446,
"three_point_percentage": 0.3,
"freethrow_percentage": 0.649
},
"points": 104,
"personal_fouls": 16,
"three_points_attempted": 20,
"minutes_played": 265,
"technical_fouls": 2,
"seconds_played": 240,
"non_player_technicals": 0,
"defensive_rebounds": 33
}
}
},
{
"team": {
"team_id": 109,
"team_name": "Sacramento",
"listings": [
{
"listing": {
"freethrows_attempted": 1,
"freethrows_made": 0,
"field_goals_attempted": 13,
"turnovers": 1,
"total_rebounds": 6,
"blocks": 0,
"steals": 0,
"assists": 4,
"field_goals_made": 6,
"offensive_rebounds": 0,
"player_first_name": "Derrick",
"three_points_made": 0,
"player_last_name": "Williams",
"points": 12,
"personal_fouls": 3,
"three_points_attempted": 2,
"minutes_played": 32,
"technical_fouls": 0,
"player_position": "F",
"player_id": 2192,
"started": "Yes",
"seconds_played": 21,
"defensive_rebounds": 6
}
},
{
"listing": {
"freethrows_attempted": 0,
"freethrows_made": 0,
"field_goals_attempted": 7,
"turnovers": 1,
"total_rebounds": 4,
"blocks": 1,
"steals": 0,
"assists": 0,
"field_goals_made": 3,
"offensive_rebounds": 2,
"player_first_name": "Jason",
"three_points_made": 0,
"player_last_name": "Thompson",
"points": 6,
"personal_fouls": 4,
"three_points_attempted": 0,
"minutes_played": 23,
"technical_fouls": 0,
"player_position": "F",
"player_id": 1496,
"started": "Yes",
"seconds_played": 11,
"defensive_rebounds": 2
}
},
{
"listing": {
"freethrows_attempted": 3,
"freethrows_made": 3,
"field_goals_attempted": 19,
"turnovers": 3,
"total_rebounds": 9,
"blocks": 3,
"steals": 0,
"assists": 6,
"field_goals_made": 11,
"offensive_rebounds": 1,
"player_first_name": "DeMarcus",
"three_points_made": 0,
"player_last_name": "Cousins",
"points": 25,
"personal_fouls": 5,
"three_points_attempted": 0,
"minutes_played": 41,
"technical_fouls": 0,
"player_position": "C",
"player_id": 1900,
"started": "Yes",
"seconds_played": 21,
"defensive_rebounds": 8
}
},
{
"listing": {
"freethrows_attempted": 3,
"freethrows_made": 3,
"field_goals_attempted": 12,
"turnovers": 2,
"total_rebounds": 4,
"blocks": 0,
"steals": 2,
"assists": 3,
"field_goals_made": 5,
"offensive_rebounds": 2,
"player_first_name": "Ben",
"three_points_made": 1,
"player_last_name": "McLemore",
"points": 14,
"personal_fouls": 3,
"three_points_attempted": 6,
"minutes_played": 34,
"technical_fouls": 0,
"player_position": "G",
"player_id": 2432,
"started": "Yes",
"seconds_played": 43,
"defensive_rebounds": 2
}
},
{
"listing": {
"freethrows_attempted": 1,
"freethrows_made": 1,
"field_goals_attempted": 12,
"turnovers": 0,
"total_rebounds": 3,
"blocks": 0,
"steals": 0,
"assists": 7,
"field_goals_made": 4,
"offensive_rebounds": 1,
"player_first_name": "Greivis",
"three_points_made": 2,
"player_last_name": "Vasquez",
"points": 11,
"personal_fouls": 2,
"three_points_attempted": 6,
"minutes_played": 35,
"technical_fouls": 0,
"player_position": "G",
"player_id": 1970,
"started": "Yes",
"seconds_played": 58,
"defensive_rebounds": 2
}
},
{
"listing": {
"freethrows_attempted": 0,
"freethrows_made": 0,
"field_goals_attempted": 4,
"turnovers": 1,
"total_rebounds": 3,
"blocks": 0,
"steals": 0,
"assists": 0,
"field_goals_made": 2,
"offensive_rebounds": 1,
"player_first_name": "Marcus",
"three_points_made": 0,
"player_last_name": "Thornton",
"points": 4,
"personal_fouls": 5,
"three_points_attempted": 2,
"minutes_played": 17,
"technical_fouls": 0,
"player_position": "G",
"player_id": 1646,
"started": "No",
"seconds_played": 26,
"defensive_rebounds": 2
}
},
{
"listing": {
"freethrows_attempted": 2,
"freethrows_made": 1,
"field_goals_attempted": 5,
"turnovers": 1,
"total_rebounds": 9,
"blocks": 0,
"steals": 0,
"assists": 0,
"field_goals_made": 3,
"offensive_rebounds": 3,
"player_first_name": "Patrick",
"three_points_made": 0,
"player_last_name": "Patterson",
"points": 7,
"personal_fouls": 4,
"three_points_attempted": 0,
"minutes_played": 26,
"technical_fouls": 0,
"player_position": "F",
"player_id": 1950,
"started": "No",
"seconds_played": 59,
"defensive_rebounds": 6
}
},
{
"listing": {
"freethrows_attempted": 0,
"freethrows_made": 0,
"field_goals_attempted": 1,
"turnovers": 2,
"total_rebounds": 1,
"blocks": 0,
"steals": 0,
"assists": 0,
"field_goals_made": 0,
"offensive_rebounds": 1,
"player_first_name": "Hamady",
"three_points_made": 0,
"player_last_name": "N`diaye",
"points": 0,
"personal_fouls": 3,
"three_points_attempted": 0,
"minutes_played": 4,
"technical_fouls": 0,
"player_position": "C",
"player_id": 1944,
"started": "No",
"seconds_played": 56,
"defensive_rebounds": 0
}
},
{
"listing": {
"freethrows_attempted": 0,
"freethrows_made": 0,
"field_goals_attempted": 2,
"turnovers": 0,
"total_rebounds": 0,
"blocks": 0,
"steals": 1,
"assists": 1,
"field_goals_made": 0,
"offensive_rebounds": 0,
"player_first_name": "Travis",
"three_points_made": 0,
"player_last_name": "Outlaw",
"points": 0,
"personal_fouls": 0,
"three_points_attempted": 1,
"minutes_played": 9,
"technical_fouls": 0,
"player_position": "F",
"player_id": 759,
"started": "No",
"seconds_played": 30,
"defensive_rebounds": 0
}
},
{
"listing": {
"player_first_name": "Chuck",
"reason": "Coach's Decision",
"player_last_name": "Hayes",
"status": "Did Not Play",
"player_id": 1044
}
},
{
"listing": {
"player_first_name": "Carl",
"reason": "Injured",
"player_last_name": "Landry",
"status": "Did Not Play",
"player_id": 1333
}
},
{
"listing": {
"player_first_name": "Ray",
"reason": "Injured",
"player_last_name": "McCallum",
"status": "Did Not Play",
"player_id": 2430
}
},
{
"listing": {
"player_first_name": "Jimmer",
"reason": "Coach's Decision",
"player_last_name": "Fredette",
"status": "Did Not Play",
"player_id": 2111
}
}
],
"totals": {
"freethrows_attempted": 10,
"freethrows_made": 8,
"field_goals_attempted": 91,
"total_rebounds": 44,
"points_off_turnovers": 15,
"blocks": 5,
"illegal_defense": 1,
"steals": 4,
"assists": 24,
"field_goals_made": 42,
"turnoveres": 15,
"offensive_rebounds": 12,
"team_turnovers": 16,
"three_points_made": 6,
"team_rebounds": 6,
"shooting_percentages": {
"field_goal_percentage": 0.462,
"three_point_percentage": 0.261,
"freethrow_percentage": 0.8
},
"points": 98,
"personal_fouls": 33,
"three_points_attempted": 23,
"minutes_played": 265,
"technical_fouls": 1,
"seconds_played": 360,
"non_player_technicals": 0,
"defensive_rebounds": 32
}
}
}
],
"away_team_final_score": 104,
"quarter": 5,
"game_date": "November 29, 2013",
"technicals": [
{
"listing": {
"flagrant": "False",
"team_id": 109,
"time_left": "8:40",
"player_first_name": "",
"quarter": 3,
"player_last_name": "",
"player_id": 0,
"def_3_sec": "True",
"bench": "False"
}
},
{
"listing": {
"flagrant": "False",
"team_id": 101,
"time_left": "6:11",
"player_first_name": "Blake",
"quarter": 3,
"player_last_name": "Griffin",
"player_id": 1590,
"def_3_sec": "False",
"bench": "False"
}
},
{
"listing": {
"flagrant": "False",
"team_id": 101,
"time_left": "5:46",
"player_first_name": "",
"quarter": 3,
"player_last_name": "",
"player_id": 0,
"def_3_sec": "True",
"bench": "False"
}
}
],
"sport": "NBA",
"visiting_team": "LA Clippers",
"status": "FINAL",
"home_team_id": 109,
"title": "LA Clippers at Sacramento",
"attendance": "17317",
"home_team_final_score": 98,
"updated_at": "2013-11-30 06:08:40 UTC",
"visiting_team_id": 101,
"home_team": "Sacramento",
"season_year": "2013",
"officials": "Tony Brothers, David Guthrie, JT Orr"
}
]
我必须将您的JSON复制到一个文件中才能正确读取它(注意,这不是必需的,您可以将JSON流直接提供给
JSON.parse
,因为它应该正确格式化),然后将其作为JSON解析为哈希数组,并使用OpenStruct将其转换为ruby对象,如果您有一个大数组,那么OpenStruct的性能不是很好,可以找到解释
由OpenStruct生成的setter和getter
a.each { |x| p x.methods(false) };
[:game_length, :game_length=, :location, :location=, :game_time, :game_time=, :season_type, :season_type=, :game_id, :game_id=, :scores_by_quarter, :scores_by_quarter=, :team_stats, :team_stats=, :away_team_final_score, :away_team_final_score=, :quarter, :quarter=, :game_date, :game_date=, :technicals, :technicals=, :sport, :sport=, :visiting_team, :visiting_team=, :status, :status=, :home_team_id, :home_team_id=, :title, :title=, :attendance, :attendance=, :home_team_final_score, :home_team_final_score=, :updated_at, :updated_at=, :visiting_team_id, :visiting_team_id=, :home_team, :home_team=, :season_year, :season_year=, :officials, :officials=]
您可以将JSON数据解析为两个类:数组和哈希:
- 阵列:
data = [1, 2] data.to_json # => "[1,2]" JSON.parse(data.to_json) # => [1, 2]
- 散列:
data = {'a' => 1} data.to_json # => "{\"a\":1}" JSON.parse(data.to_json) # => {"a"=>1}
data
的变量中:
返回包含数据的哈希,在您的示例中,该哈希就是所有数据。如果存在多个散列,则使用以下方法对其进行迭代:data。首先
或:data.each { |hash| ... }
data.first['scores_by_quarter'].last['scores']['home_team_score']
# => 4
减少数据大小。扔掉这么大的样品没什么好处,除非你想让人们的眼睛发晕。
data.map { |hash| ... }
data.first['scores_by_quarter'].last['scores']['home_team_score']
# => 4