Python新手求助:如何将足球赛事JSON转为自定义对象结构?
嘿,作为Python OOP新手,你碰到的这个场景太常见了——把结构化的足球赛事JSON转换成有明确关联的自定义对象,这样后续的统计计算、数据累积和数据库存储都会清晰很多。我来一步步帮你拆解,从类的定义到JSON解析,再到对象关联,最后聊聊累积统计和数据库的实现思路。
第一步:先定义对应业务的Python类
首先,我们要把JSON里的每个实体(球员、点球、进球、球队、赛事)都转换成Python类,这样每个对象的属性和关联关系都能清晰定义。这里要重点处理对象间的关联(比如点球属于哪个球员,进球的射手和助攻是谁):
class Player: def __init__(self, nr, surname, name, role): self.nr = nr # 球衣号 self.surname = surname self.name = name self.role = role self.is_starting = False # 是否首发,后续标记 self.penalties = [] # 该球员的所有点球 self.goals = [] # 该球员的所有进球 self.assists = [] # 该球员的所有助攻 class Penalty: def __init__(self, time, nr): self.time = time # 点球时间 self.player_nr = nr # 罚球球员号码 self.player = None # 后续关联到Player对象 class Goal: def __init__(self, time, scorer_nr, assist_nrs=None, shot_on_goal="N"): self.time = time self.scorer_nr = scorer_nr # 射手号码 self.assist_nrs = assist_nrs or [] # 助攻球员号码列表 self.shot_on_goal = shot_on_goal self.scorer = None # 关联射手Player对象 self.assisters = [] # 关联助攻Player对象列表 class Team: def __init__(self, name): self.name = name self.players = {} # 用球衣号做键:{nr: Player对象},方便快速查找 self.starting_lineup = [] # 首发Player对象列表 self.penalties = [] # 球队所有点球 self.goals = [] # 球队所有进球 # 统计字段,后续自动计算 self.total_goals = 0 self.total_penalties = 0 class Game: def __init__(self, time, spectators, location, referees=None): self.time = time # 比赛日期 self.spectators = spectators # 观众人数 self.location = location # 比赛场地 self.referees = referees or [] # 裁判列表 self.main_referee = None # 主裁判 self.teams = [] # 两支参赛Team对象
第二步:解析JSON,映射对象并建立关联
接下来写一个解析函数,把JSON数据逐个转换成上面的类实例,同时手动建立对象间的关联(比如把点球绑定到对应的球员)。这里要注意JSON里的字段可能是单个字典或列表(比如Penalty可能是一个或多个),需要做类型判断:
def parse_team(team_data): """解析单支球队的JSON数据,返回Team对象""" team = Team(team_data["TeamName"]) # 1. 先创建所有球员,存入球队的players字典 for player_data in team_data["Players"]["Player"]: player = Player( nr=player_data["Nr"], surname=player_data["Surname"], name=player_data["Name"], role=player_data["Role"] ) team.players[player.nr] = player # 2. 标记首发球员 for starter_data in team_data["StartingLineUp"]["Player"]: nr = starter_data["Nr"] if nr in team.players: team.players[nr].is_starting = True team.starting_lineup.append(team.players[nr]) # 3. 解析点球,关联到对应球员 if "Penalties" in team_data: penalty_raw = team_data["Penalties"]["Penalty"] # 处理单个点球(字典)或多个点球(列表) penalties = penalty_raw if isinstance(penalty_raw, list) else [penalty_raw] for p_data in penalties: penalty = Penalty(time=p_data["Time"], nr=p_data["Nr"]) team.penalties.append(penalty) team.total_penalties += 1 # 关联到球员 if penalty.player_nr in team.players: penalty.player = team.players[penalty.player_nr] penalty.player.penalties.append(penalty) # 4. 解析进球,关联射手和助攻球员 if "Goals" in team_data: goal_raw = team_data["Goals"]["VG"] goals = goal_raw if isinstance(goal_raw, list) else [goal_raw] for g_data in goals: # 处理助攻球员(可能是单个字典或列表) assist_nrs = [] if "P" in g_data: p_data = g_data["P"] if isinstance(p_data, list): assist_nrs = [item["Nr"] for item in p_data] else: assist_nrs = [p_data["Nr"]] goal = Goal( time=g_data["Time"], scorer_nr=g_data["Nr"], assist_nrs=assist_nrs, shot_on_goal=g_data.get("ShotOnGoal", "N") ) team.goals.append(goal) team.total_goals += 1 # 关联射手 if goal.scorer_nr in team.players: goal.scorer = team.players[goal.scorer_nr] goal.scorer.goals.append(goal) # 关联助攻球员 for nr in goal.assist_nrs: if nr in team.players: assister = team.players[nr] goal.assisters.append(assister) assister.assists.append(goal) # 球员替换(PlayerChanges)可以按需扩展,比如记录替换时间和球员 return team def parse_game(json_data): """解析整个赛事的JSON数据,返回Game对象""" game_raw = json_data["Game"] # 处理裁判和主裁判 referees = [f"{ref['Name']} {ref['Surname']}" for ref in game_raw.get("T", [])] main_referee = f"{game_raw['VT']['Name']} {game_raw['VT']['Surname']}" if "VT" in game_raw else None game = Game( time=game_raw["Time"], spectators=game_raw["Spectators"], location=game_raw["Location"], referees=referees ) game.main_referee = main_referee # 解析两支球队 for team_data in game_raw["Team"]: team = parse_team(team_data) game.teams.append(team) return game
第三步:累积统计与数据库存储
现在你可以轻松处理多场赛事的累积统计了,比如维护一个全局字典记录每个球员的生涯数据,然后把赛事和球员数据存入MySQL:
累积统计示例
# 全局字典,用(名字,姓氏)作为唯一键(你说姓名组合唯一) all_player_cumulative = {} def update_cumulative_stats(game): """更新球员的累积统计数据""" for team in game.teams: for player in team.players.values(): key = (player.name, player.surname) if key not in all_player_cumulative: # 初始化新球员的累积数据 all_player_cumulative[key] = { "total_goals": 0, "total_penalties": 0, "total_assists": 0, "games_played": 0, "starts": 0 } # 更新统计值 stats = all_player_cumulative[key] stats["total_goals"] += len(player.goals) stats["total_penalties"] += len(player.penalties) stats["total_assists"] += len(player.assists) stats["games_played"] += 1 if player.is_starting: stats["starts"] += 1
MySQL存储示例
先确保安装了mysql-connector-python(pip install mysql-connector-python),然后写存储函数:
import mysql.connector def save_game_to_db(game, cumulative_stats): """把赛事数据和累积统计存入MySQL""" # 连接数据库(替换成你的配置) conn = mysql.connector.connect( host="localhost", user="your_username", password="your_password", database="football_db" ) cursor = conn.cursor() # 1. 插入赛事记录 insert_game_sql = """ INSERT INTO games (match_time, spectators, location, main_referee) VALUES (%s, %s, %s, %s) """ cursor.execute(insert_game_sql, (game.time, game.spectators, game.location, game.main_referee)) game_id = cursor.lastrowid # 获取刚插入的赛事ID,用于关联球队 # 2. 插入球队记录 for team in game.teams: insert_team_sql = """ INSERT INTO teams (game_id, team_name, total_goals, total_penalties) VALUES (%s, %s, %s, %s) """ cursor.execute(insert_team_sql, (game_id, team.name, team.total_goals, team.total_penalties)) team_id = cursor.lastrowid # 获取球队ID,用于关联球员 # 3. 插入球员记录(包含累积统计) for player in team.players.values(): key = (player.name, player.surname) stats = cumulative_stats[key] insert_player_sql = """ INSERT INTO players (team_id, jersey_nr, first_name, last_name, role, is_starting, total_goals, total_penalties, total_assists, games_played, starts) VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s) """ cursor.execute(insert_player_sql, ( team_id, player.nr, player.name, player.surname, player.role, player.is_starting, stats["total_goals"], stats["total_penalties"], stats["total_assists"], stats["games_played"], stats["starts"] )) # 提交事务并关闭连接 conn.commit() cursor.close() conn.close()
第四步:完整使用流程
import json # 读取单场赛事JSON文件 with open("match1.json", "r", encoding="utf-8") as f: match_json = json.load(f) # 解析成Game对象 game = parse_game(match_json) # 更新累积统计 update_cumulative_stats(game) # 存入数据库 save_game_to_db(game, all_player_cumulative) # 重复以上步骤处理其他JSON文件即可
为什么不用字典?
你担心字典无法处理关联是对的——字典是扁平的键值对,每次查找球员或关联点球都要手动遍历,代码会越来越乱。而自定义对象可以把数据和行为封装在一起,比如你可以给Player类加一个方法:
def get_total_contributions(self): """计算球员的总贡献:进球+助攻+点球""" return len(self.goals) + len(self.assists) + len(self.penalties)
这样后续统计会非常方便,扩展性也强。
关于marshmallow
marshmallow是用来简化序列化/反序列化的,但对于OOP新手来说,手动解析能让你更清晰地理解对象之间的关联逻辑。等你熟悉了类和对象的关系后,再去尝试marshmallow会更容易上手——它本质上是帮你自动完成“JSON转对象”的映射,不用手动写解析函数。
内容的提问来源于stack exchange,提问作者Oskars Sjomkāns

