xmind&百度脑图之导入导出
背景
应公司业务要求,开发一款支持多人在线编辑思维导图的产品,且要做到思维导图文件导入导出。前端组件 vue-testcase-minder-editor
,
组件作者:https://github.com/chenhengjie123/vue-testcase-minder-editor?tab=readme-ov-file
数据格式对比
#百度脑图数据格式
{'data': {'id': 'b7697411-883d-4494-933d-7da80c3fc63f', 'created': 1722849031301, 'text': '导出'
}, 'children': [
{'data': {'id': '2ecec96e-7364-4ea9-a2c1-dec85180a919', 'created': 1722849031301, 'text': '导出主题 1'
}, 'children': [
{'data': {'id': 'fb9a7734-19da-475f-9c9d-4ce152db3df8', 'created': 1722849031301, 'text': '哈哈哈哈哈'
}, 'children': []
},
{'data': {'id': '7b0ffb3e-fafd-42d2-b62e-20ff3e657ab0', 'created': 1722849031301, 'text': '高大上的v'
}, 'children': []
}
]
},
{'data': {'id': 'cbf24130-3b2d-4a7a-bbd1-28a85cc0f514', 'created': 1722849031301, 'text': '导出主题 2'
}, 'children': []
}
]
}
# xmind数据格式
[
{'title': '画布 1', 'topic': {'title': '导出', 'topics': [
{'title': '导出主题 1', 'topics': [
{'title': '哈哈哈哈哈', 'id': '6c4bbf9b-981c-44c7-9aa6-16d0c65a40f9'
},
{'title': '高大上的v', 'id': 'ea0e2537-9d51-4fdc-99df-13e44976a881'
}
], 'id': 'f7391556c0c84b1d9c259b582e'
},
{'title': '导出主题 2', 'id': '42b829374fcbb84ccd7af93f02'
}
], 'id': '17da8339e188988536c24ba1ce'
}, 'structure': 'org.xmind.ui.map.clockwise', 'id': 'bada207e86cb919b28692b4656'
}
]
导入--xmind转百度脑图数据格式
# 处理xmind导入数据格式
def transform_data(input_data):
def transform_node(node):
transformed_node = {
'data': {
'id': generate_unique_id(), # You need to implement generate_unique_id()
'created': generate_timestamp(), # You need to implement generate_timestamp()
'text': node['text']
},
'children': [transform_node(child) for child in node.get('children', [])]
}
return transformed_node
# Helper functions to generate unique IDs and timestamps (epoch time in milliseconds)
def generate_unique_id():
# You can use UUID or any other method to generate unique IDs
# Example implementation using UUID:
import uuid
return str(uuid.uuid4())
import time
def generate_timestamp():
# Generate current time in milliseconds
return int(round(time.time() * 1000))
# Start transformation from the root node
transformed_data = transform_node(input_data['data'])
return transformed_data
## 导入接口
def importParticulars(self, request, **kwargs):
# 获取当前脚本的文件路径
script_path = os.path.abspath(__file__)
# 获取当前脚本的目录(不包含文件名)
script_dir = os.path.dirname(script_path)
# 构建当前目录下的file文件路径
file_path = os.path.join(script_dir, 'file/')
# print(file_path)
# 清空file文件夹
folder_to_clear = file_path
clear_folder(folder_to_clear)
fs = FileSystemStorage(location=file_path)
particulars_id = request.data["particulars_id"]
file_data = request.data["file_data"]
# 接收前端传入数据存到file文件夹中 .xmind格式
uploaded_file_name = fs.save(file_data.name, file_data)
xmindparser.config = {
'showTopicId': True, # 是否展示主题ID
'hideEmptyValue': True # 是否隐藏空值
}
# 文件存的路径
filePath = os.path.join(file_path, uploaded_file_name)
# .xmind格式转成 .json格式文件
content = xmindparser.xmind_to_json(filePath)
# 读取json文件
with open(content, 'r', encoding='utf-8') as f:
data = json.load(f)
# 判断是否空文件
if len(data) == 0:
return APIRespones('1001', '空文件不可导入~', False)
res = data[0]['topic']
# 将res(字典格式)转成 JSON 字符串
original_json = '''{}'''.format(res)
# 使用正则表达式进行替换
converted_json = re.sub(r"'title':", "'created':'','text':", original_json)
# 将单引号转换为双引号,以符合 JSON 格式要求
converted_json = converted_json.replace("'", '"')
converted_json = re.sub(r'"([^"]+)"[\s\S]"([^"]+)"', r'\1\2', converted_json)
# print("converted_json",converted_json)
# converted_json = process_data(converted_json)
# 将转换后的 JSON 字符串加载为 Python 对象(字典)
converted_data = json.loads(converted_json)
# 再转一次(topics --> children)
original_json2 = '''{}'''.format(converted_data)
converted_json = re.sub(r"'topics':", "'children':", original_json2)
converted_json = converted_json.replace("'", '"')
converted_data2 = json.loads(converted_json)
finalData = {}
finalData['data'] = converted_data2
# 调用封装好的transform_data方法转数据格式,得到最终的前端组件可以渲染的数据格式
transformed_data = transform_data(finalData)
xmind_particulars.objects.filter(pk=particulars_id).update(file_data=transformed_data)
data = xmind_particulars.objects.filter(pk=particulars_id).values()
return APIRespones('1000', '文件导入成功~', True, data)
导入--百度脑图转xmind数据格式,并下载文件到本地
# 导出相关方法封装
"""
# 通过下方递归方法来改造以上这段代码
设置xmind文件主题以及子主题
for subtopic in map['children']:
print(9999,subtopic)
topic1 = root_topic1.addSubTopic()
topic1.setTitle(subtopic['data']['text'])
for subtopic2 in subtopic['children']:
print(888,subtopic2)
topic2 = topic1.addSubTopic()
topic2.setTitle(subtopic2['data']['text'])
for subtopic3 in subtopic2['children']:
print(777, subtopic3)
topic3 = topic2.addSubTopic()
topic3.setTitle(subtopic3['data']['text'])
for subtopic4 in subtopic3['children']:
print(777, subtopic4)
topic4 = topic3.addSubTopic()
topic4.setTitle(subtopic4['data']['text'])
"""
# 通过递归来改造以上这段代码
def add_subtopics(parent_topic, subtopics):
for subtopic in subtopics:
new_topic = parent_topic.addSubTopic()
new_topic.setTitle(subtopic['data']['text'])
# 如果有子主题,递归调用
if 'children' in subtopic:
add_subtopics(new_topic, subtopic['children'])
"""
踩坑:xmind python代码save后用xmind2020报错无法打开:原因xmind8格式无法用xmind2020打开,META-INF文件夹缺失,使用以下方法可解决
***************************************
xmind改成zip文件,
解压后 在目录内创建 META-INF文件夹
META-INF文件夹内创建 manifest.xml文件
文件内容:
'<?xml version="1.0" encoding="UTF-8" standalone="no"?><manifest xmlns="urn:xmind:xmap:xmlns:manifest:1.0" password-hint=""></manifest>'
重新打包后zip文件 然后后缀改成 xmind
"""
def repair(fname):
zip_file = zipfile.ZipFile(fname, 'a')
zip_file.writestr('META-INF/manifest.xml', '<?xml version="1.0" encoding="UTF-8" standalone="no"?><manifest xmlns="urn:xmind:xmap:xmlns:manifest:1.0" password-hint=""></manifest>')
zip_file.close()
# 导出xmind接口
def exportMindMap(self, request, **kwargs):
# 前端上传的文件数据
map = request.data["map"]
# 获取当前脚本的文件路径
script_path = os.path.abspath(__file__)
# 获取当前脚本的目录(不包含文件名)
script_dir = os.path.dirname(script_path)
# exportFile文件夹路径
exportFile_path = os.path.join(script_dir, 'exportFile')
# files = glob.glob(os.path.join(exportFile_path, '*.xmind'))
# # 删除后缀为 .xmind 的文件
# for file in files:
# os.remove(file)
clear_folder(exportFile_path) # 先清空exportFile文件
# 定义xmind文件名称
filename = 'output.xmind'
# 构建exportFile目录下的 'output.xmind' 文件路径
file_path = os.path.join(exportFile_path, filename)
# 创建一个新的xmind文件或加载已有xmind文件
workbook = xmind.load(file_path)
# 获取第一个画布
first_sheet = workbook.getPrimarySheet()
# 设置画布名称
first_sheet.setTitle(map['data']['text'])
# 获取画布中心主题,默认创建画布时会新建一个空白中心主题
root_topic1 = first_sheet.getRootTopic()
# 设置主题名称
root_topic1.setTitle(map['data']['text'])
# map['children'] 是最顶层的子主题
add_subtopics(root_topic1, map['children'])
# 保存并覆盖原始文件
xmind.save(workbook)
# 日志
develop.log.info('这是一个info级别的日志'+ file_path)
# 此方法解决生成的xmind文件本地使用xmind8之后的版本无法打开的问题
repair(file_path)
# 检查文件是否存在
if not os.path.exists(file_path):
return HttpResponse(status=404)
# 导出文件命名
xmindFileName = map['data']['text']
# 返回文件响应
response = FileResponse(open(file_path, 'rb'), content_type='application/xmind')
response['Content-Disposition'] = f'attachment; filename="{xmindFileName}"'
return response
python操作xmind文件
XMind-1.2.0.tar.gz
下载地址:
https://files.pythonhosted.org/packages/7c/8c/e13a82fa9b0394c0d58248196d7d51d7274407cdebc1df36b76034ab990d/XMind-1.2.0.tar.gz
创建及更新xmind文件
运行结果
解析xmind文件
解析xmind文件
#!/usr/bin/env python
# -*- coding:utf-8 -*-
import json
import xmind
import pipes
def dict_to_prettify_json(data):
print(json.dumps(data, indent=4, separators=(',', ': ')))
def custom_parse_xmind(workbook):
elements = {}
def _echo(tag, element, indent=0):
title = element.getTitle()
elements[element.getID()] = title
print('\t' * indent, tag, ':', pipes.quote(title))
def dump_sheet(sheet):
root_topic = sheet.getRootTopic()
_echo('RootTopic', root_topic, 1)
for topic in root_topic.getSubTopics() or []:
_echo('AttachedSubTopic', topic, 2)
for topic in root_topic.getSubTopics(xmind.core.const.TOPIC_DETACHED) or []:
_echo('DetachedSubtopic', topic, 2)
for rel in sheet.getRelationships():
id1, id2 = rel.getEnd1ID(), rel.getEnd2ID()
print('Relationship: [%s] --> [%s]' % (elements.get(id1), elements.get(id2)))
# 遍历画布
for sheet in workbook.getSheets():
_echo('Sheet', sheet)
dump_sheet(sheet)
# 加载已有xmind文件,如果不存在,则新建
workbook = xmind.load('D:\\example\\example.xmind')
print(workbook.getData()) # 获取整个xmind数据(字典的形式)
dict_to_prettify_json(workbook.getData())
# 获取某个画布的数据(字典的形式)
first_sheet = workbook.getPrimarySheet()
dict_to_prettify_json(first_sheet.getData())
# 获取某个主题数据(字典的形式)
root_topic = first_sheet.getRootTopic()
dict_to_prettify_json(root_topic.getData())
# 获取评论数据
commentsbook = workbook.commentsbook
print(commentsbook.getData())
# 自定义解析
custom_parse_xmind(workbook)