2. Java-MarkDown文件解析-工具类

news2025/1/30 16:59:10

2. Java-MarkDown文件解析-工具类

1. 思路

  1. 读取markdown文件的内容,根据markdown的语法进行各个类型语法的解析。
  2. 引入工具类 commonmark 和 commonmark-ext-gfm-tables进行markdown语法解析。

2. 工具类

pom.xml

<!-- commonmark 解析markdown -->
<dependency>
    <groupId>org.commonmark</groupId>
    <artifactId>commonmark</artifactId>
    <version>0.21.0</version>
</dependency>
<!-- commonmark 解析markdown tables -->
<dependency>
    <groupId>org.commonmark</groupId>
    <artifactId>commonmark-ext-gfm-tables</artifactId>
    <version>0.21.0</version>
</dependency>

MarkdownParseResult

import com.alibaba.fastjson.JSONObject;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;

/**
 * Markdown解析结果
 * @Author: onecomer
 * @Date: 2021/1/26 16:00
 */
@ApiModel(description = "Markdown解析结果")
@Data
public class MarkdownParseResult {

    @ApiModelProperty(value = "MarkdownHtml内容")
    private String htmlContent;

    @ApiModelProperty(value = "标题及内容集合",
            example = "{'标题1':'内容1','标题2':'内容2',...}")
    private JSONObject titles;

    @ApiModelProperty(value = "表格标题及内容集合",
            example = "{'表格标题':'{headers:['列1','列2'],rows:[{'值1','值2'},{'值1','值2'}]}",
            notes = "headers为表头,rows为行数据")
    private JSONObject tables;


    @ApiModelProperty(value = "无序列表集合",
            example = "{''无序列表标题:[{'无序列表内容1'},{'无序列表内容2'},...]}")
    private JSONObject unOrderedLists;


    @ApiModelProperty(value = "有序列表集合",
            example = "{''有序列表标题:[{'有序列表内容1'},{'有序列表内容2'},...]}")
    private JSONObject orderedLists;


    @ApiModelProperty(value = "代码块集合",
            example = "{'代码块标题1':{'codeBlockContent(固定值)':'代码块内容1','codeBlockType(固定值)':'代码块类型1'}}")
    private JSONObject codeBlocks;
}

MarkdownParserUtil

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import org.commonmark.ext.gfm.tables.TableBlock;
import org.commonmark.ext.gfm.tables.TableBody;
import org.commonmark.ext.gfm.tables.TableCell;
import org.commonmark.ext.gfm.tables.TableHead;
import org.commonmark.ext.gfm.tables.TableRow;
import org.commonmark.ext.gfm.tables.TablesExtension;
import org.commonmark.node.AbstractVisitor;
import org.commonmark.node.BulletList;
import org.commonmark.node.FencedCodeBlock;
import org.commonmark.node.Heading;
import org.commonmark.node.ListItem;
import org.commonmark.node.Node;
import org.commonmark.node.OrderedList;
import org.commonmark.node.Paragraph;
import org.commonmark.node.SoftLineBreak;
import org.commonmark.node.Text;
import org.commonmark.parser.Parser;
import org.commonmark.renderer.html.HtmlRenderer;

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * Markdown解析工具类
 * @Author: onecomer
 * @Date: 2021/1/26 16:00
 */
public class MarkdownParserUtil {

    private static Parser parser;
    private static HtmlRenderer renderer;

    static {
        parser = Parser.builder()
                .extensions(Arrays.asList(TablesExtension.create()))
                .build();
        renderer = HtmlRenderer.builder()
                .extensions(Arrays.asList(TablesExtension.create()))
                .build();
    }

    /**
     * 读取并解析Markdown文件
     */
    public static MarkdownParseResult parseMarkdownFile(String filePath) {
        String content = null;
        try {
            content = new String(Files.readAllBytes(Paths.get(filePath)));
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
        MarkdownParseResult result = parseMarkdownFileContent(content);
        return result;
    }

    /**
     * 解析Markdown内容
     */
    public static MarkdownParseResult parseMarkdownFileContent(String content) {
        Node document = parser.parse(content);
        MarkdownParseResult result = new MarkdownParseResult();
//        result.setHtmlContent(renderer.render(document));

        JSONObject titles = new JSONObject(true);
        JSONObject tables = new JSONObject(true);
        JSONObject unOrderedLists = new JSONObject(true);
        JSONObject orderedLists = new JSONObject(true);
        JSONObject codeBlocks = new JSONObject(true);

        String currentHeading = "";
        StringBuilder paragraphContent = new StringBuilder();
        boolean isDelReapeatTilte = true;

        Node node = document.getFirstChild();
        while (node != null) {
            if (node instanceof Heading) {

                // 标题
                String text = getText(node);
                currentHeading = text;
                // 只有标题的直接添加
                if (!((node.getNext()) instanceof Paragraph)) {
                    titles.put(currentHeading, currentHeading);
                }

            } else if (node instanceof Paragraph) {

                String text = getText(node);
                // 普通文本
                paragraphContent.append(text).append("\n");

                // 结束添加paragraphContent
                if (!((node.getNext()) instanceof Paragraph)) {
                    titles.put(currentHeading, paragraphContent.toString().trim());
                    paragraphContent = new StringBuilder();
                }

            } else if (node instanceof BulletList) {

                // 无序列表
                JSONArray items = new JSONArray();
                Node listItem = node.getFirstChild();
                while (listItem != null) {
                    if (listItem instanceof ListItem) {
                        String text = getText(listItem);
                        items.add(text);
                    }
                    listItem = listItem.getNext();
                }
                unOrderedLists.put(currentHeading, items);
                if (isDelReapeatTilte) {
                    titles.remove(currentHeading);
                }

            } else if (node instanceof OrderedList) {

                // 有序列表
                JSONArray items = new JSONArray();
                Node listItem = node.getFirstChild();
                while (listItem != null) {
                    if (listItem instanceof ListItem) {
                        String text = getText(listItem);
                        items.add(text);
                    }
                    listItem = listItem.getNext();
                }
                orderedLists.put(currentHeading, items);
                if (isDelReapeatTilte) {
                    titles.remove(currentHeading);
                }

            } else if (node instanceof FencedCodeBlock) {

                // 代码块
                FencedCodeBlock codeBlock = (FencedCodeBlock) node;
                JSONObject codeBlockInfo = new JSONObject(true);
                String codeBlockContent = codeBlock.getLiteral();
                String codeBlockType = codeBlock.getInfo();
                codeBlockInfo.put("codeBlockContent", codeBlockContent);
                codeBlockInfo.put("codeBlockType", codeBlockType);
                codeBlocks.put(currentHeading, codeBlockInfo);
                if (isDelReapeatTilte) {
                    titles.remove(currentHeading);
                }

            } else if (node instanceof TableBlock) {

                // 表格
                JSONObject tableInfo = new JSONObject(true);
                JSONArray headers = new JSONArray();
                JSONArray rows = new JSONArray();

                // TableHead
                Node row = node.getFirstChild();
                if (row instanceof TableHead) {
                    Node headerRow = row.getFirstChild();
                    if (headerRow instanceof TableRow) {
                        Node cell = headerRow.getFirstChild();
                        while (cell != null) {
                            if (cell instanceof TableCell) {
                                String text = getText(cell);
                                headers.add(text);
                            }
                            cell = cell.getNext();
                        }
                    }
                }

                // TableBody
                Node tableBody = row.getNext();
                while (tableBody != null) {
                    if (tableBody instanceof TableBody) {
                        // TableRow
                        Node tableRow = tableBody.getFirstChild();
                        while (tableRow != null) {
                            if (tableRow instanceof TableRow) {
                                JSONArray rowData = new JSONArray();
                                Node tableCell = tableRow.getFirstChild();
                                while (tableCell != null) {
                                    if (tableCell instanceof TableCell) {
                                        String text = getText(tableCell);
                                        rowData.add(text);
                                    }
                                    tableCell = tableCell.getNext();
                                }
                                rows.add(rowData);
                            }
                            tableRow = tableRow.getNext();
                        }
                    }
                    tableBody = tableBody.getNext();
                }

                tableInfo.put("headers", headers);
                tableInfo.put("rows", rows);
                tables.put(currentHeading, tableInfo);
                if (isDelReapeatTilte) {
                    titles.remove(currentHeading);
                }
            }
            // 处理下一个节点
            node = node.getNext();
        }

        result.setTitles(titles);
        result.setTables(tables);
        result.setUnOrderedLists(unOrderedLists);
        result.setOrderedLists(orderedLists);
        result.setCodeBlocks(codeBlocks);

        return result;
    }

    /**
     * 获取节点的文本内容(包含格式)
     */
    private static String getText(Node node) {
        StringBuilder sb = new StringBuilder();
        node.accept(new AbstractVisitor() {
            @Override
            public void visit(Text text) {
                sb.append(text.getLiteral());
            }

//            @Override
//            public void visit(Emphasis emphasis) {
//                sb.append("*").append(getText(emphasis)).append("*");
//            }
//
//            @Override
//            public void visit(StrongEmphasis strongEmphasis) {
//                sb.append("**").append(getText(strongEmphasis)).append("**");
//            }
//
//            @Override
//            public void visit(Code code) {
//                sb.append("`").append(code.getLiteral()).append("`");
//            }
        });
        return sb.toString().trim();
    }

    /**
     * 判断是否为表格节点
     */
    @Deprecated
    private static boolean isTable(Node node) {
//        String content = getTextContent(node);
//        return content.contains("|") && content.contains("\n") && content.contains("---");
        Node firstChild = node.getFirstChild();
        Node secondChild = firstChild != null ? firstChild.getNext() : null;
        if (secondChild instanceof SoftLineBreak) {
            return true;
        }
        return false;
    }

    /**
     * 解析表格内容
     */
    @Deprecated
    private static JSONObject parseTable(Node tableNode) {

        String[] lines = getText(tableNode).split("\n");
        boolean isHeader = true;
        List<String> headers = new ArrayList<>();

        for (String line : lines) {
            line = line.trim();
            if (line.isEmpty() || line.startsWith("|---")) {
                isHeader = false;
                continue;
            }

            String[] cells = line.split("\\|");
            List<String> cleanCells = new ArrayList<>();
            for (String cell : cells) {
                String cleaned = cell.trim();
                if (!cleaned.isEmpty()) {
                    cleanCells.add(cleaned);
                }
            }

            if (isHeader) {
                headers.addAll(cleanCells);
//                tableInfo.setHeaders(headers);
            } else {
                Map<String, String> row = new HashMap<>();
                for (int i = 0; i < headers.size() && i < cleanCells.size(); i++) {
                    row.put(headers.get(i), cleanCells.get(i));
                }
//                tableInfo.getRows().add(row);
            }
        }
        return null;
    }


    public static void main(String[] args) {
        String filePath = "D:\\tab\\1_ideaIC-2022.2.win\\2_WS\\1_SAAS_hgit_2\\2_assembler_all\\3_biz-project\\api-manage\\markdown\\新增实体数据能力.md";
//            filePath = "D:\\tab\\1_ideaIC-2022.2.win\\2_WS\\1_SAAS_hgit_2\\2_assembler_all\\3_biz-project\\api-manage\\markdown\\新增实体数据能力2.md";

        MarkdownParseResult result = parseMarkdownFile(filePath);

//            System.out.println("HTML内容:");
//            System.out.println(result.getHtmlContent());

        System.out.println("\n标题及内容:");
        System.out.println(result.getTitles().toJSONString());

        System.out.println("\n表格内容:");
        System.out.println(result.getTables().toJSONString());

        System.out.println("\n无序列表:");
        System.out.println(result.getUnOrderedLists().toJSONString());

        System.out.println("\n有序列表:");
        System.out.println(result.getOrderedLists().toJSONString());

        System.out.println("\n代码块:");
        System.out.println(result.getCodeBlocks().toJSONString());
    }
} 

3. 测试

测试结果

image-20250126155507486

image-20250126155201114

image-20250126155245693

markdown文件

# 新增实体数据能力



## 接口说明

用来新增一条记录

用来新增一条记录2

用来新增一条记录3



## 资产类型:API

## 应用

- 应用S码:Sxx

## 标签

- 新增实体数据能力
- xxx系统
- Sxx
- 新增记录 数据管理

## 版本

- v1.0.0



## 接口地址

```json
{
    "测试地址": "{baseUrl}/model/{dataSource}/{entityName}/add",
    "生产地址": "{baseUrl}/model/{dataSource}/{entityName}/add"
}
```

## 调用前提

```json
需要先部署低代码引擎微服务,部署文档链接如下:
开发环境引擎部署:https://ihaier.feishu.cn/wiki/LyitwBYg4i8fRDkpPC0crxpMnlg
运行环境引擎部署:https://ihaier.feishu.cn/wiki/ZG16wdmOiib658k39X1cuzlKnSe
```

## 请求方式

POST

## 请求头Header

| 参数名       | 类型   | 是否必填 | 参数说明                      |
| :----------- | :----- | :------- | :---------------------------- |
| Access-Token | String | 是       | 统一登录token,从账号中心获取 |
| Access-Token2 | String | 是       | 统一登录token,从账号中心获取 |

## 请求参数类型

@RequestBody

## 请求参数

| 参数名     | 是否必填 | 类型         | 描述                    |
| :--------- | :------- | :----------- | :---------------------- |
| field1     | 否       | String       | 字段1                   |
| field2     | 否       | Integer      | 字段2                   |
| entityName | 否       | Object/Array | 关联实体(一对一/一对多) |

### 请求参数示例

```json
{
    "field1": "",
    "field2": 19,
    "entityName": {
        "field1": "",
        "field2": ""
    },
    "entityName": [{
        "field1": "",
        "field2": ""
    }]
}
```

## 返回参数类型

@ResponseBody

## 返回参数

| 参数名  | 类型    | 说明                         |
| :------ | :------ | :--------------------------- |
| code    | Integer | 响应码,0表示成功,非0表示失败 |
| message | String  | 提示消息                     |
| data    | Object  | 返回数据                     |
| data.id | String  | 主键ID                       |

### 返回参数示例

#### 正确

```json
{
   "code": 0,
   "message": "success", 
   "data": {
       "id": "主键ID"
   } 
}
```

#### 错误

```json
{
    "code": 400,
    "message": "请求参数错误"
}
```

### 错误码

| errorCode | errorMessage |
| :-------- | :----------- |
| 400       | 请求参数错误 |

## 调用示例

```json
// 请求示例
POST /model/myDataSource/User/add
{
    "name": "张三",
    "age": 25,
    "department": {
        "id": "dept001",
        "name": "技术部"
    }
}

// 返回结果示例
{
    "code": 0,
    "message": "success",
    "data": {
        "id": "user001"
    }
}
```

## 实现逻辑

### 时序图

```mermaid
sequenceDiagram
    participant Client
    participant API
    participant Service
    participant Database
    
    Client->>API: POST /model/{ds}/{entity}/add
    API->>Service: addEntity(data)
    Service->>Service: validateData(data)
    Service->>Database: insert(data)
    Database-->>Service: Return id
    Service-->>API: Return result
    API-->>Client: Return response
```

### 业务逻辑

1. 校验请求参数的合法性
2. 根据实体定义验证字段
3. 生成主键ID
4. 保存实体数据
5. 返回新增记录的ID

### 三方服务

无 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2286517.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

研发的立足之本到底是啥?

0 你的问题&#xff0c;我知道&#xff01; 本文深入T型图“竖线”的立足之本&#xff1a;专业技术 技术赋能业务能力。研发在学习投入精力最多&#xff0c;也误区最多。 某粉丝感发展遇到瓶颈&#xff0c;项目都会做&#xff0c;但觉无提升&#xff0c;想跳槽。于是&#x…

react中如何获取dom元素

实现代码 const inputRef useRef(null) inputRef.current.focus()

19.Word:小马-校园科技文化节❗【36】

目录 题目​ NO1.2.3 NO4.5.6 NO7.8.9 NO10.11.12索引 题目 NO1.2.3 布局→纸张大小→页边距&#xff1a;上下左右插入→封面&#xff1a;镶边→将文档开头的“黑客技术”文本移入到封面的“标题”控件中&#xff0c;删除其他控件 NO4.5.6 标题→原文原文→标题 正文→手…

DeepSeek回答量化策略对超短线资金的影响

其实最近很长一段时间我在盘中的感受就是量化策略的触发信号都是超短线选手经常用到的,比如多个题材相互卡位,近期小红书-消费,好想你,来伊份 跟 算力 电光科技,机器人 金奥博 冀凯股份 五洲新春 建设工业 ,这些票的波动其实都是被量化策略锚定了,做成了策略异动。特别…

无耳科技 Solon v3.0.7 发布(2025农历新年版)

Solon 框架&#xff01; Solon 框架由杭州无耳科技有限公司&#xff08;下属 Noear 团队&#xff09;开发并开源。是新一代&#xff0c;面向全场景的 Java 企业级应用开发框架。从零开始构建&#xff08;非 java-ee 架构&#xff09;&#xff0c;有灵活的接口规范与开放生态。…

常见的多媒体框架(FFmpeg GStreamer DirectShow AVFoundation OpenMax)

1.FFmpeg FFmpeg是一个非常强大的开源多媒体处理框架&#xff0c;它提供了一系列用于处理音频、视频和多媒体流的工具和库。它也是最流行且应用最广泛的框架&#xff01; 官方网址&#xff1a;https://ffmpeg.org/ FFmpeg 的主要特点和功能&#xff1a; 编解码器支持: FFmpe…

本地部署Deepseek R1

最近Deepseek R1模型也是彻底火出圈了&#xff0c; 但是线上使用经常会受到各种限制&#xff0c;有时候还会连不上&#xff0c;这里我尝试本地部署了Deepseek 的开源R1模型&#xff0c;具体的操作如下&#xff1a; 首先登陆ollama平台&#xff0c;Ollama.ollama平台是一个开源…

深入解析 .NET 命名管道技术, 通过 Sharp4Tokenvator 实现本地权限提升

01. 管道访问配置 在 .NET 中通常使用 PipeSecurity 类为管道设置访问控制规则&#xff0c;用于管理命名管道的访问控制列表&#xff08;ACL&#xff09;。通过这个类&#xff0c;可以为命名管道配置精细化的安全权限&#xff0c;从而控制哪些用户或用户组能够访问管道&#x…

Cesium ArcGisMapServerImageryProvider API 介绍

作为一名GIS研究生&#xff0c;WebGIS 技术无疑是我们必学的核心之一。说到WebGIS&#xff0c;要提的就是 Cesium —— 这个让3D地球可视化变得简单又强大的工具。为了帮助大家更好地理解和使用 Cesium&#xff0c;我决定把我自己在学习 Cesium 文档过程中的一些心得和收获分享…

登录授权流程

发起一个网络请求需要&#xff1a;1.请求地址 2.请求方式 3.请求参数 在检查中找到request method&#xff0c;在postman中设置同样的请求方式将登录的url接口复制到postman中&#xff08;json类型数据&#xff09;在payload中选择view parsed&#xff0c;将其填入Body-raw中 …

Python设计模式 - 组合模式

定义 组合模式&#xff08;Composite Pattern&#xff09; 是一种结构型设计模式&#xff0c;主要意图是将对象组织成树形结构以表示"部分-整体"的层次结构。这种模式能够使客户端统一对待单个对象和组合对象&#xff0c;从而简化了客户端代码。 组合模式有透明组合…

【深度学习】图像分类数据集

图像分类数据集 MNIST数据集是图像分类中广泛使用的数据集之一&#xff0c;但作为基准数据集过于简单。 我们将使用类似但更复杂的Fashion-MNIST数据集。 %matplotlib inline import torch import torchvision from torch.utils import data from torchvision import transfo…

【四川乡镇界面】图层shp格式arcgis数据乡镇名称和编码2020年wgs84无偏移内容测评

本文将详细解析标题和描述中提到的IT知识点&#xff0c;主要涉及GIS&#xff08;Geographic Information System&#xff0c;地理信息系统&#xff09;技术&#xff0c;以及与之相关的文件格式和坐标系统。 我们要了解的是"shp"格式&#xff0c;这是一种广泛用于存储…

ubuntu解决普通用户无法进入root

项目场景&#xff1a; 在RK3566上移植Ubuntu20.04之后普通用户无法进入管理员模式 问题描述 在普通用户使用sudo su试图进入管理员模式的时候报错 解决方案&#xff1a; 1.使用 cat /etc/passwd 查看所有用户.最后一行是 若无用户&#xff0c;则使用 sudo useradd -r -m -s /…

第3章 基于三电平空间矢量的中点电位平衡策略

0 前言 在NPC型三电平逆变器的直流侧串联有两组参数规格完全一致的电解电容,由于三电平特殊的中点钳位结构,在进行SVPWM控制时,在一个完整开关周期内,直流侧电容C1、C2充放电不均匀,各自存储的总电荷不同,电容电压便不均等,存在一定的偏差。在不进行控制的情况下,系统无…

网络工程师 (8)存储管理

一、页式存储基本原理 &#xff08;一&#xff09;内存划分 页式存储首先将内存物理空间划分成大小相等的存储块&#xff0c;这些块通常被称为“页帧”或“物理页”。每个页帧的大小是固定的&#xff0c;例如常见的页帧大小有4KB、8KB等&#xff0c;这个大小由操作系统决定。同…

实验一---典型环节及其阶跃响应---自动控制原理实验课

一 实验目的 1.掌握典型环节阶跃响应分析的基本原理和一般方法。 2. 掌握MATLAB编程分析阶跃响应方法。 二 实验仪器 1. 计算机 2. MATLAB软件 三 实验内容及步骤 利用MATLAB中Simulink模块构建下述典型一阶系统的模拟电路并测量其在阶跃响应。 1.比例环节的模拟电路 提…

【BQ3568HM开发板】如何在OpenHarmony上通过校园网的上网认证

引言 前面已经对BQ3568HM开发板进行了初步测试&#xff0c;后面我要实现MQTT的工作&#xff0c;但是遇到一个问题&#xff0c;就是开发板无法通过校园网的认证操作。未认证的话会&#xff0c;学校使用的深澜软件系统会屏蔽所有除了认证用的流量。好在我们学校使用的认证系统和…

PythonFlask框架

文章目录 处理 Get 请求处理 POST 请求应用 app.route(/tpost, methods[POST]) def testp():json_data request.get_json()if json_data:username json_data.get(username)age json_data.get(age)return jsonify({username: username测试,age: age})从 flask 中导入了 Flask…

【电工基础】1.电能来源,触电伤害,触电预防,触电急救

一。电能来源 1.电能来源 发电-》输电-》变电-》配电 2.分配电 一类负荷 如果供电中断会造成生命危险&#xff0c;造成国民经济的重大损失&#xff0c;损坏生产的重要设备以致使生产长期不能恢复或产生大量废品&#xff0c;破坏复杂的工艺过程&#xff0c;以及破坏大…