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
sequenceDiagramparticipant Clientparticipant APIparticipant Serviceparticipant DatabaseClient->>API: POST /model/{ds}/{entity}/addAPI->>Service: addEntity(data)Service->>Service: validateData(data)Service->>Database: insert(data)Database-->>Service: Return idService-->>API: Return resultAPI-->>Client: Return response
```### 业务逻辑1. 校验请求参数的合法性
2. 根据实体定义验证字段
3. 生成主键ID
4. 保存实体数据
5. 返回新增记录的ID### 三方服务无