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
### 三方服务
无