如何实现思维导图xmind和opml/md格式互转

大家在工作中经常需要通过思维导图来梳理自己的思路,比如通过xmind或processon等在线工具来拆解主题、问题、内容、方法等等,但思维导图软件默认形成的.xmind格式是二进制格式,如果后续需要在其他地方复用,这种格式文件复用起来特别不方便,一般情况下需要将其转换为opml(或xml)、md等格式。因此本次主要结合AI编写了一个小工具,实现xmind和opml格式这两种格式的互转,这样既可以在思维导图软件中查看编辑,也可以通过obsidian进行查看编辑,目前已经编写沉淀了50多款转换工具,其他转换工具可以参考之前的文章。本次主要内容如下,供大家参考。

一、主要功能

主要基于xml、zipfile、json等库,实现将xmind格式思维导图转换为opml或md格式功能,以及反过来的转换功能。

具体支持功能如下:

1.通过xml和json实现XMind转为OPML文件格式,详见xmind_to_opml()

2.通过xml和zipfile实现 .XMind转换为 .md 文件格式,详见xmind_to_markdown()

3.通过zipfile和json实现 .OPML转换为 .XMind文件格式,详见make_xmind()

二、实现效果
1.原始xmind导图
2.转换后的opml格式内容截图
3.转换后的markdown格式内容截图
三、核心代码
复制代码
#!/root/anaconda3/envs/llm/bin/python  
# -*- coding: utf-8 -*-  
import xml.etree.ElementTree as ET  
import zipfile  
import json,sys  
import uuid  
from xml.sax.saxutils import escape  
import os  
"""  
解析大纲内容  
:param outline_node: 根目录大纲名称  
:param topic: 输出 每一级大纲名称和id  
"""  
def parse_outline(outline_node):  
    title = outline_node.attrib.get('text', '')  
    topic = {  
        "id": str(uuid.uuid4()),  
        "title": title  
    }  
    children = []  
    for child in outline_node:  
        if child.tag == 'outline':  
            children.append(parse_outline(child))  
  
    if children:  
        topic["children"] = {  
            "attached": children  
        }  
    return topic  
"""  
把OPML转成XMind文件  
:param opml_file: OPML文件路径 或者 OPML的XML文本字符串 
:param output_file: 输出 .xmind 文件路径  
"""  
def make_xmind(opml_file, output_file):  
    # 判断是否为文件路径并读取  
    try:  
        with open(opml_file, "r", encoding="utf-8") as f:  
            opml_content = f.read()  
    except FileNotFoundError:  
        # 文件不存在,就当成xml字符串处理(原有逻辑)  
        opml_content = opml_file  
  
    root_elem = ET.fromstring(opml_content)  
    body_elem = root_elem.find('body')  
    first_outline = body_elem.find('outline')  
  
    root_topic_data = parse_outline(first_outline)  
    root_sheet_id = str(uuid.uuid4())  
    root_topic_data["id"] = root_sheet_id  
  
    content_data = [  
        {  
            "id": root_sheet_id,  
            "title": root_topic_data["title"],  
            "rootTopic": root_topic_data  
        }  
    ]  
  
    manifest_data = {  
        "fileVersion": "v2",  
        "rootSheetId": root_sheet_id  
    }  
  
    metadata_data = {  
        "data": {  
            "creator": {  
                "name": "hhue",  
                "version": "v1.0"  
            }  
        }  
    }  
  
    # output_file = "咨询主题思维导图_转换自OPML.xmind"  
    with zipfile.ZipFile(output_file, 'w', zipfile.ZIP_DEFLATED) as zf:  
        zf.writestr('content.json', json.dumps(content_data, ensure_ascii=False))  
        zf.writestr('manifest.json', json.dumps(manifest_data, ensure_ascii=False))  
        zf.writestr('metadata.json', json.dumps(metadata_data, ensure_ascii=False))  
  
    print(f"Generated: {output_file}")  
"""  
读取 xmind 文件,返回根节点树 [(text, [children])]:param path: xmind文件路径 或者 xind文本字符串,  
"""  
def read_xmind_nodes(path):  
    z = zipfile.ZipFile(path)  
    names = z.namelist()  
  
    # 新版 XMind (JSON)    if 'content.json' in names:  
        data = json.loads(z.read('content.json'))[0]  
  
        def conv(node):  
            text = node.get('title', '')  
            kids = [conv(c) for c in node.get('children', {}).get('attached', [])]  
            return (text, kids)  
  
        return conv(data['rootTopic'])  
  
    # 旧版 XMind (XML)    elif 'content.xml' in names:  
        root = ET.fromstring(z.read('content.xml'))  
        topic = root.find('.//topic')  
  
        def conv(t):  
            text = t.find('title').text or ''  
            kids = [conv(c) for c in t.findall('topic')]  
            return (text, kids)  
  
        return conv(topic)  
    else:  
        raise ValueError('不是有效的 xmind 文件')  
"""  
组织输出opml文件的数据内容  
:param node: 当前节点  
"""  
def to_opml(node, indent=0):  
    text, children = node  
    pad = '  ' * indent  
    out = [f'{pad}<outline text="{escape(text)}">']  
    for c in children:  
        out.append(to_opml(c, indent + 1))  
    out.append(f'{pad}</outline>')  
    return '\n'.join(out)  
"""  
把XMind转成OPML文件  
:param xmind_file: .xmind 文件路径  
:param opml_file: 输出,OPML文件路径 或者 OPML的XML文本字符串
"""  
def xmind_to_opml(xmind_file,opml_file='output.opml'):  
    # xmind_file = '你的文件.xmind'  
    root = read_xmind_nodes(xmind_file)  
    opml = f'''<?xml version="1.0" encoding="UTF-8"?>  
    <opml version="1.0">      <head><title>{escape(root[0])}</title></head>  
      <body>    {to_opml(root)}  
      </body>    </opml>'''  
    with open(opml_file, 'w', encoding='utf-8') as f:  
        f.write(opml)  
    print('转换完成:',opml_file)  
"""  
递归解析 XMind 的 topic 节点,转换为 markdown 列表项  
:param topic: 根主题  
:param 输出: 具备层级的 markdown 列表  
"""  
def parse_topic(topic, level=0):  
    lines = []  
    title = topic.get("title", "").strip()  
    if title:  
        # 根据层级生成对应缩进的 Markdown 列表  
        indent = "  " * level  
        lines.append(f"{indent}- {title}")  
  
    # 查找子主题 (适配不同版本的 XMind JSON 结构)  
    children_container = topic.get("children", {})  
    # 有些版本子主题在 attached 数组中  
    attached_children = children_container.get("attached", [])  
  
    for child in attached_children:  
        lines.extend(parse_topic(child, level + 1))  
  
    return lines  
"""  
将 .xmind 文件转换为 .md 文件  
:param xmind_path: xmind文件路径  
:param md_path: markdown文件路径  
"""  
def xmind_to_markdown(xmind_path, md_path):  
    if not os.path.exists(xmind_path):  
        print(f"错误: 找不到文件 {xmind_path}")  
        return  
  
    markdown_lines = []  
  
    # .xmind 文件是 zip 压缩包  
    with zipfile.ZipFile(xmind_path, 'r') as zf:  
        if "content.json" not in zf.namelist():  
            print("错误: 有效的 XMind 文件中未找到 content.json")  
            return  
  
        # 读取 content.json        content_content = zf.read("content.json").decode("utf-8")  
        sheets = json.loads(content_content)  
  
        # 遍历每一个工作表 (Sheet)        for sheet_idx, sheet in enumerate(sheets):  
            sheet_title = sheet.get("title", f"Sheet {sheet_idx + 1}")  
            markdown_lines.append(f"# {sheet_title}\n")  
  
            # 获取根主题 (rootTopic)            root_topic = sheet.get("rootTopic")  
            if root_topic:  
                # 解析根主题及所有子孙节点  
                topic_lines = parse_topic(root_topic, level=0)  
                markdown_lines.extend(topic_lines)  
  
            markdown_lines.append("\n---\n")  
  
    # 写入 Markdown 文件  
    with open(md_path, "w", encoding="utf-8") as f:  
        f.write("\n".join(markdown_lines))  
  
    print(f"转换成功!Markdown 文件已保存至: {md_path}")  
  
# 运行示例:  
# python xml2Xmind.py E:\导图.xmind E:\导图.opml xmind2opml
# python xml2Xmind.py E:\导图.xmind E:\导图.md xmind2md
if __name__ == "__main__":  
    if len(sys.argv) < 2:  
        print("用法: python xml2Xmind.py <xml文件路径> [输出xind文件路径] [类型选项]")  
        print("\n示例:")  
        print("  python xml2Xmind.py book.xml book.xmind opml2xmind   ")  
        sys.exit  
  
    in_path = sys.argv[1]  
    output_path = sys.argv[2]  
    type = sys.argv[3]  
    if type == 'opml2xmind':  
        # xml-->xmind  
        make_xmind(in_path, output_path)  
        print('xml-->xmind转换完成...')  
    elif type == 'xmind2opml':          
        # xmind-->xml  
        xmind_to_opml(in_path,output_path)  
        print('xmind-->xml转换完成...')  
    elif type == 'xmind2md':  
        # xmind-->md  
        xmind_to_markdown(in_path, output_path)  
        print('xmind-->md转换完成...')  
    else:  
        print("无效的类型选项,请使用 'opml2xmind'、'xmind2opml' 或 'xmind2md'")
相关推荐
xiami_world21 天前
Xmind、Miro、博思白板AI怎么选?思维导图/流程图自动生成
人工智能·ai·信息可视化·流程图·xmind
一马平川的大草原1 个月前
pdf转md的三种方式
pdf·ocr·md
SuperHeroWu71 个月前
鸿蒙AICoding项目实践SDD运用
md·需求·aicoding·sdd·规格
PFFstronger2 个月前
基于 Dify 知识库 + Ollama 大模型,自动生成测试用例并导出 XMind 的完整方案
人工智能·python·测试用例·xmind
Blockbuater_drug2 个月前
Amber分子动力学模拟10: 蛋白-核酸复合物模拟操作
md·分子动力学模拟·amber·核酸·蛋白-核酸复合物·ol15力场·nastruct
wabil2 个月前
[AI-Talk] codx制作xmind文件技巧
ide·ai编程·myeclipse·xmind
北漂燕郊杨哥2 个月前
DotMD:一款基于 Go + Wails 的轻量开源 Markdown 桌面编辑器
golang·开源·编辑器·markdown·md
霸道流氓气质4 个月前
CodeBuddy中使用 MCP 工具将 XMind 思维导图转换为 Markdown —— 实操流程汇总
git·myeclipse·xmind
测试员周周5 个月前
【Appium 系列】第17节-XMind用例转换 — 从思维导图到 YAML
java·服务器·人工智能·单元测试·appium·测试用例·xmind