Python:合并带点分隔键的嵌套字典并输出JSON的问题
元数据合并问题解决方案
数据结构介绍
从扫描电镜(SEM)采集数据中提取三类元数据:
acquisitionMetadata:普通字典,键为点分隔字符串,代表层级结构datasetMetadata:同结构字典的列表,每个字典对应一个采集任务的数据集元数据imageMetadata:字典列表,每个元素对应一个数据集,且包含另一个字典列表,代表该数据集下每张图片的元数据
需求说明
需要在Python中将三类字典合并为嵌套字典(最终输出为JSON文件),键代表层级关系。例如'acquisition.dataset.images.creationTime': '18.08.2020 17:51:07'需转换为以下嵌套结构:
acquisition { dataset { images { creationTime: '18.08.2020 17:51:07' } } }
问题
现有代码无法正确生成dataset和images数组,要么重复已存在的acquisition、dataset、image键,要么将图片字典放在数据集数组外。AI生成的代码存在硬编码层级问题,输出有冗余键。
期望输出结构
合并后的字典需符合如下嵌套结构:
metadata = { 'acquisition': { 'genericMetadata': { 'program': { 'programName': 'Auto Slice & View 4', 'programVersion': '4.2.1.1982' }, 'applicationId': { 'identifierValue': 'ASV' }, 'fileVersion': '1.2', 'projectName': '20200818_AlSi13 XRM tomo2', 'numberOfCuts': '719' }, 'dataset': [ { 'rows': '1', 'columns': '1', 'images': [ { 'creationTime': '18.08.2020 17:51:07', 'stage': { 'workingDistance': { 'value': '0.00403678' } } }, { 'creationTime': '18.08.2020 18:09:06', 'stage': { 'workingDistance': { 'value': '0.00403773' } } } ] }, { 'rows': '1', 'columns': '1', 'images': [ { 'creationTime': '18.08.2020 17:51:07', 'stage': { 'workingDistance': { 'value': '0.00403678' } } }, { 'creationTime': '18.08.2020 18:09:06', 'stage': { 'workingDistance': { 'value': '0.00403773' } } } ] } ] } }
最小可复现输入
acquisition_metadata = { 'acquisition.genericMetadata.program.programName': 'Auto Slice & View 4', 'acquisition.genericMetadata.program.programVersion': '4.2.1.1982', 'acquisition.genericMetadata.applicationId.identifierValue': 'ASV', 'acquisition.genericMetadata.fileVersion': '1.2', 'acquisition.genericMetadata.projectName': '20200818_AlSi13 XRM tomo2', 'acquisition.genericMetadata.numberOfCuts': '719', } dataset_metadata = [ { 'acquisition.dataset.rows': '1', 'acquisition.dataset.columns': '1', }, { 'acquisition.dataset.rows': '1', 'acquisition.dataset.columns': '1', }, ] image_metadata = [ [ { 'acquisition.dataset.images.creationTime': '18.08.2020 17:51:07', 'acquisition.dataset.images.stage.workingDistance.value': '0.00403678', }, { 'acquisition.dataset.images.creationTime': '18.08.2020 18:09:06', 'acquisition.dataset.images.stage.workingDistance.value': '0.00403773', } ], [ { 'acquisition.dataset.images.creationTime': '18.08.2020 17:51:07', 'acquisition.dataset.images.stage.workingDistance.value': '0.00403678', }, { 'acquisition.dataset.images.creationTime': '18.08.2020 18:09:06', 'acquisition.dataset.images.stage.workingDistance.value': '0.00403773', } ] ]
已尝试代码(存在冗余键问题)
import json import os def combine_metadata(acquisition_metadata, dataset_metadata, image_metadata): metadata = {} # Combine acquisition metadata for key, value in acquisition_metadata.items(): nested_keys = key.split('.') current_dict = metadata for nested_key in nested_keys[:-1]: if nested_key not in current_dict: current_dict[nested_key] = {} current_dict = current_dict[nested_key] current_dict[nested_keys[-1]] = value # Combine dataset metadata metadata['acquisition']['dataset'] = [] for dataset in dataset_metadata: dataset_dict = {} for key, value in dataset.items(): nested_keys = key.split('.') current_dict = dataset_dict for nested_key in nested_keys[:-1]: if nested_key not in current_dict: current_dict[nested_key] = {} current_dict = current_dict[nested_key] current_dict[nested_keys[-1]] = value metadata['acquisition']['dataset'].append(dataset_dict) # Combine image metadata for i, images in enumerate(image_metadata): metadata['acquisition']['dataset'][i]['images'] = [] for image in images: image_dict = {} for key, value in image.items(): nested_keys = key.split('.') current_dict = image_dict for nested_key in nested_keys[:-1]: if nested_key not in current_dict: current_dict[nested_key] = {} current_dict = current_dict[nested_key] current_dict[nested_keys[-1]] = value metadata['acquisition']['dataset'][i]['images'].append(image_dict) return metadata def save_metadata_as_json(metadata, save_path): filename = os.path.join(save_path, "combined.json") with open(filename, 'w') as file: json.dump(metadata, file, indent=4) print(f"Metadata saved as {filename}")
问题输出示例
{ "acquisition": { "genericMetadata": { "program": { "programName": "Auto Slice & View 4", "programVersion": "4.2.1.1982" }, "applicationId": { "identifierValue": "ASV" }, "fileVersion": "1.2", "projectName": "20200818_AlSi13 XRM tomo2", "numberOfCuts": "719" }, "dataset": [ { "acquisition": { "dataset": { "rows": "1", "columns": "1" } }, "images": [ { "acquisition": { "dataset": { "images": { "creationTime": "18.08.2020 17:51:07", "stage": { "workingDistance": { "value": "0.00403678" } } } } } }, { "acquisition": { "dataset": { "images": { "creationTime": "18.08.2020 18:09:06", "stage": { "workingDistance": { "value": "0.00403773" } } } } } } ] }, { "acquisition": { "dataset": { "rows": "1", "columns": "1" } }, "images": [ { "acquisition": { "dataset": { "images": { "creationTime": "18.08.2020 17:51:07", "stage": { "workingDistance": { "value": "0.00403678" } } } } } }, { "acquisition": { "dataset": { "images": { "creationTime": "18.08.2020 18:09:06", "stage": { "workingDistance": { "value": "0.00403773" } } } } } } ] } ] } }
修正后的代码
核心思路是处理dataset和image的键时,忽略前缀的acquisition.dataset或acquisition.dataset.images,直接解析剩余的层级路径:
import json import os def parse_nested_key(key, prefix): """去除键的指定前缀,返回剩余的层级键列表""" if key.startswith(prefix): return key[len(prefix)+1:].split('.') return key.split('.') def build_nested_dict(flat_dict, prefix=""): """将扁平化字典转换为嵌套字典,可指定要忽略的前缀""" nested_dict = {} for key, value in flat_dict.items(): keys = parse_nested_key(key, prefix) current = nested_dict for k in keys[:-1]: if k not in current: current[k] = {} current = current[k] current[keys[-1]] = value return nested_dict def combine_metadata(acquisition_metadata, dataset_metadata, image_metadata): metadata = build_nested_dict(acquisition_metadata) # 处理dataset列表 metadata['acquisition']['dataset'] = [] for ds in dataset_metadata: ds_dict = build_nested_dict(ds, prefix="acquisition.dataset") metadata['acquisition']['dataset'].append(ds_dict) # 处理image列表,与dataset一一对应 for i, img_list in enumerate(image_metadata): metadata['acquisition']['dataset'][i]['images'] = [] for img in img_list: img_dict = build_nested_dict(img, prefix="acquisition.dataset.images") metadata['acquisition']['dataset'][i]['images'].append(img_dict) return metadata def save_metadata_as_json(metadata, save_path): filename = os.path.join(save_path, "combined.json") with open(filename, 'w') as file: json.dump(metadata, file, indent=4) print(f"Metadata saved as {filename}")
验证代码
if __name__ == "__main__": combined = combine_metadata(acquisition_metadata, dataset_metadata, image_metadata) print(json.dumps(combined, indent=4)) save_metadata_as_json(combined, "./")
运行后输出将完全符合期望的嵌套结构,无冗余键。
内容的提问来源于stack exchange,提问作者mathwiz97
相关产品推荐
相关产品推荐

