当前位置: 首页 > article >正文

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

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.kler.cn/a/523019.html

相关文章:

  • 登录授权流程
  • Oracle Primavera P6 最新版 v24.12 更新 1/2
  • 2025-01-28 - 通用人工智能技术 - RAG - 本地安装 DeepSeek-R1对话系统 - 流雨声
  • Spring Security(maven项目) 3.0.2.9版本
  • 【Linux探索学习】第二十七弹——信号(一):Linux 信号基础详解
  • Next.js 14 TS 中使用jwt 和 App Router 进行管理
  • 20【变量的深度理解】
  • 最大值的期望 与 期望的最大值
  • mysql学习笔记-事务基础知识
  • 渗透测试之WAF规则触发绕过规则之规则库绕过方式
  • Linux进程调度与等待:背后的机制与实现
  • 大数据学习之Kafka消息队列、Spark分布式计算框架一
  • AWS SimSpace Weaver
  • 如何在本地部署deepseek r1模型?
  • 物业软件推动物业行业数字化转型 实现高效管理和优质客户体验
  • 【PyTorch】6.张量形状操作:在深度学习的 “魔方” 里,玩转张量形状
  • Couchbase UI: Query
  • 具身智能技术趋势
  • 编程语言中的常见Bug及解决方案
  • 2024年除夕
  • Hive存储系统全面测试报告
  • MATLAB中处理大数据的技巧与方法
  • 计算机网络之计算机网络的定义、组成与功能
  • 如何靠Python完成设备巡检
  • 数字电子技术(十六)——CMOS反相器以及CMOS反相器的电路结构和工作原理
  • python-leetcode-翻转二叉树