You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何对比多份JSON文件与固定结构JSON?含列表多实例场景

JSON结构校验优化:支持列表多实例场景

问题背景

我们需要对比多份JSON文件与固定结构模板,生成包含Expected、Actual、Result的Excel报告。现有代码在处理列表多实例场景时存在两处核心问题:

  • 当列表包含多个结构一致的实例时,代码误判为Fail(原逻辑会重复提取相同键,但需求是只要实例结构匹配就标记Pass)
  • 当某个列表实例出现键错误时,无法精准定位到具体实例,仅能标记整体文件为Fail

固定结构模板示例:

{
   "School" : 
  {
    "Teachers" : [ 
        {
            "Subject" : { },
            "Experience" : { },
            "Date of Birth" : { }
        } 
    ],
    "Class_Rooms" : { },
    "Playgrounds" : { },
    "Staff" : [ 
        {
            "Year-2021" : { },
            "Year-2022" : { }
        }
    ]
  }
}

优化后完整代码

import json
import os
import pandas as pd
from typing import List, Dict

def get_structure_template(obj, path: str = "") -> Dict:
    """提取JSON对象的结构模板,列表仅保留第一个元素的结构作为统一标准"""
    if isinstance(obj, dict):
        return {
            k: get_structure_template(v, f"{path}.{k}" if path else k)
            for k, v in obj.items()
        }
    elif isinstance(obj, list):
        if not obj:
            return []
        # 列表模板取第一个元素的结构,代表所有元素应遵循的格式
        return [get_structure_template(obj[0], f"{path}[*]")]
    else:
        # 非容器类型,用类型名称作为占位符
        return type(obj).__name__

def validate_structure(actual_obj, template_obj, path: str = "") -> List[Dict]:
    """递归校验实际对象是否符合模板,返回详细错误列表"""
    errors = []
    if isinstance(template_obj, dict):
        if not isinstance(actual_obj, dict):
            errors.append({
                "Expected Path": path,
                "Actual Path": path,
                "Expected Type": "dict",
                "Actual Type": type(actual_obj).__name__,
                "Result": "Fail"
            })
            return errors
        
        # 检查缺失的键
        missing_keys = set(template_obj.keys()) - set(actual_obj.keys())
        for key in missing_keys:
            full_path = f"{path}.{key}" if path else key
            errors.append({
                "Expected Value": full_path,
                "Actual Value": None,
                "Result": "Fail"
            })
        
        # 检查多余的键
        extra_keys = set(actual_obj.keys()) - set(template_obj.keys())
        for key in extra_keys:
            full_path = f"{path}.{key}" if path else key
            errors.append({
                "Expected Value": None,
                "Actual Value": full_path,
                "Result": "Fail"
            })
        
        # 递归检查每个键的子结构
        for key in template_obj.keys():
            if key not in actual_obj:
                continue
            sub_path = f"{path}.{key}" if path else key
            errors.extend(validate_structure(actual_obj[key], template_obj[key], sub_path))
    
    elif isinstance(template_obj, list):
        if not isinstance(actual_obj, list):
            errors.append({
                "Expected Path": path,
                "Actual Path": path,
                "Expected Type": "list",
                "Actual Type": type(actual_obj).__name__,
                "Result": "Fail"
            })
            return errors
        
        if not template_obj:
            # 模板是空列表,实际列表任意内容都允许
            return errors
        
        list_item_template = template_obj[0]
        # 逐个校验列表中的每个实例
        for idx, item in enumerate(actual_obj):
            sub_path = f"{path}[{idx}]" if path else f"[{idx}]"
            errors.extend(validate_structure(item, list_item_template, sub_path))
    
    else:
        # 基础类型校验
        actual_type = type(actual_obj).__name__
        if actual_type != template_obj:
            errors.append({
                "Expected Path": path,
                "Actual Path": path,
                "Expected Type": template_obj,
                "Actual Type": actual_type,
                "Result": "Fail"
            })
    
    return errors

def format_errors_to_rows(errors: List[Dict]) -> List[Dict]:
    """将错误列表转换为Excel报告的标准行格式"""
    rows = []
    for err in errors:
        if "Expected Value" in err:
            rows.append({
                "Expected Value": err["Expected Value"],
                "Actual Value": err["Actual Value"],
                "Result": err["Result"]
            })
        else:
            # 类型错误的情况,补充类型信息
            rows.append({
                "Expected Value": f"{err['Expected Path']} ({err['Expected Type']})",
                "Actual Value": f"{err['Actual Path']} ({err['Actual Type']})",
                "Result": err["Result"]
            })
    return rows

# 配置路径(请替换为实际路径)
json_folder = "Path/to/your/json/files"
structure_folder = "Path/to/your/structure/file"

# 加载并生成结构模板
with open(os.path.join(structure_folder, "structure.json"), "r") as f:
    json_structure = json.load(f)
structure_template = get_structure_template(json_structure)

# 初始化汇总表数据
summary_data = []

# 遍历所有JSON文件进行校验
for filename in os.listdir(json_folder):
    if not filename.endswith(".json"):
        continue
    
    file_path = os.path.join(json_folder, filename)
    with open(file_path, "r") as f:
        try:
            actual_data = json.load(f)
        except json.JSONDecodeError:
            summary_data.append({
                "Json filename": filename,
                "Status": "Invalid JSON"
            })
            continue
    
    # 执行结构校验
    errors = validate_structure(actual_data, structure_template)
    
    if not errors:
        summary_data.append({
            "Json filename": filename,
            "Status": "Pass"
        })
    else:
        summary_data.append({
            "Json filename": filename,
            "Status": "Fail"
        })
        # 生成当前文件的详细错误报告
        report_rows = format_errors_to_rows(errors)
        df = pd.DataFrame(report_rows)
        df.to_excel(f"{os.path.splitext(filename)[0]}_errors.xlsx", index=False)

# 生成最终汇总报告
summary_df = pd.DataFrame(summary_data)
with pd.ExcelWriter("Summary_report.xlsx") as writer:
    summary_df.to_excel(writer, sheet_name="Summary", index=False)

关键优化点

  • 结构模板提取:get_structure_template函数专门提取JSON的结构框架,列表仅保留第一个元素的结构作为统一标准,忽略实例数量,完全匹配"只要结构匹配就通过"的需求。
  • 递归精准校验:validate_structure函数递归遍历每个层级,对于列表会逐个校验每个实例的结构,错误信息会精准定位到具体的实例索引(如School.Teachers[1].suject)。
  • 错误信息标准化:将类型不匹配、键缺失/多余等错误统一格式化为Excel兼容的行数据,清晰展示Expected、Actual和Result字段。
  • 健壮性提升:新增JSON解析异常处理,将格式错误的文件标记为"Invalid JSON",避免程序崩溃。

内容的提问来源于stack exchange,提问作者Manish saini

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.24 07:27:55