"""Decode Excel/CSV/JSON source files for a no-side-effect mapping preview.""" import csv,io,json from openpyxl import load_workbook MAX_ROWS=1000 def parse_table(name,data): if len(data)>12_000_000:raise ValueError('文件超过12 MB') lower=name.lower() if lower.endswith('.csv'): rows=list(csv.DictReader(io.StringIO(data.decode('utf-8-sig')))) elif lower.endswith('.json'): rows=json.loads(data.decode('utf-8-sig')) if isinstance(rows,dict):rows=rows.get('rows',rows.get('entries',rows.get('items',[]))) elif lower.endswith('.xlsx'): wb=load_workbook(io.BytesIO(data),read_only=True,data_only=True) try: ws=wb.active;iterator=ws.iter_rows(values_only=True) header=next(iterator,None) if header is None:raise ValueError('Excel 工作表为空') titles=[str(x or '').strip() for x in header] if not all(titles) or len(set(titles))!=len(titles):raise ValueError('Excel 列标题不能为空或重复') rows=[] for i,values in enumerate(iterator): if i>=MAX_ROWS:raise ValueError('一次最多导入1000条记录') if any(x is not None and str(x).strip() for x in values): rows.append(dict(zip(titles,values))) finally:wb.close() else:raise ValueError('支持 CSV、JSON 或 XLSX 文件') if not isinstance(rows,list) or not 1<=len(rows)<=MAX_ROWS or any(not isinstance(r,dict) for r in rows): raise ValueError('内容必须包含1-1000条对象记录') keys=list(dict.fromkeys(str(k) for r in rows for k in r)) if not keys or len(keys)>150:raise ValueError('字段数量不合法') # Date/datetime -> string to avoid JSON encoder serialization exceptions. normalized=[] for row in rows: normalized.append({str(k):v.isoformat() if hasattr(v,'isoformat') else v for k,v in row.items()}) return {'filename':name,'columns':keys,'count':len(rows),'rows':normalized}