2. Java-MarkDown文件解析-工具类
1. 思路
- 读取markdown文件的内容,根据markdown的语法进行各个类型语法的解析。
- 引入工具类 commonmark 和 commonmark-ext-gfm-tables进行markdown语法解析。
2. 工具类
pom.xml
<dependency>
<groupId>org.commonmark</groupId>
<artifactId>commonmark</artifactId>
<version>0.21.0</version>
</dependency>
<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;
@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;
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();
}
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;
}
public static MarkdownParseResult parseMarkdownFileContent(String content) {
Node document = parser.parse(content);
MarkdownParseResult result = new MarkdownParseResult();
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");
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();
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();
}
}
}
Node tableBody = row.getNext();
while (tableBody != null) {
if (tableBody instanceof TableBody) {
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());
}
});
return sb.toString().trim();
}
@Deprecated
private static boolean isTable(Node node) {
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);
} 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));
}
}
}
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";
MarkdownParseResult result = parseMarkdownFile(filePath);
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. 测试
测试结果
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
### 三方服务
无