Java 操作 Markdown(2)–flexmark-java 使用


flexmark-java 是一个基于 Java 实现的 CommonMark (spec 0.28) 解析器,它采用了“先块后内联”的 Markdown 解析架构;本文主要介绍其基本使用,文中所使用到的软件版本:Java 17.0.5、flexmark 0.64.8。

1、简介

flexmark-java 的优势在于速度快、灵活性高、基于 Markdown 源元素的抽象语法树(AST)能够精确到组成元素的单个字符词素的源位置,以及强大的可扩展性。

flexmark-java 允许对解析过程进行细粒度控制,并针对安装大量扩展的解析场景进行了优化。解析器和扩展都提供了丰富的选项,用于调整解析器行为和 HTML 渲染变体。最终目标是让解析器和渲染器能够以极高的精度模拟其他解析器。现在,通过实现 “Markdown 处理器仿真” 功能,这一目标已部分完成。

该项目的动机是为了替换 JetBrains IDEs 的 Markdown Navigator 插件中使用的 pegdown 解析器。pegdown 的功能集非常强大,但其整体速度并不理想,并且在遇到病态输入时,解析过程会挂起或实际上处于挂起状态。

对于 0.62.2 及以下版本:需要 Java 8+;对于 0.64.0 及以上版本:需要 Java 11+。

2、flexmark-java 使用

2.1、样例文件

解析 Markdown 时使用该样例文件。

## 1 Markdown语法教程

### 1.1 标题

不同数量的`#`可以完成不同的标题,如下:

# 一级标题

## 二级标题

### 三级标题

### 1.2 字体

粗体、斜体、粗体和斜体,删除线,需要在文字前后加不同的标记符号。如下:

**这个是粗体**

*这个是斜体*

***这个是粗体加斜体***

~~这里是删除线~~

注:如果想给字体换颜色、字体或者居中显示,需要使用内嵌HTML来实现。

### 1.3 无序列表

无序列表的使用,在符号`-`后加空格使用。如下:

- 无序列表 1
- 无序列表 2
- 无序列表 3

如果要控制列表的层级,则需要在符号`-`前使用空格。如下:

- 无序列表 1
- 无序列表 2
  - 无序列表 2.1
  - 无序列表 2.2

### 1.4 有序列表

有序列表的使用,在数字及符号`.`后加空格后输入内容,如下:

1. 有序列表 1
2. 有序列表 2
3. 有序列表 3

### 1.5 引用

引用的格式是在符号`>`后面书写文字。如下:

> 读一本好书,就是在和高尚的人谈话。 ——歌德

> 雇用制度对工人不利,但工人根本无力摆脱这个制度。 ——阮一峰

### 1.7 链接

微信公众号仅支持公众号文章链接,即域名为`https://mp.weixin.qq.com/`的合法链接。使用方法如下所示:

对于该论述,欢迎读者查阅之前发过的文章,[你是《未来世界的幸存者》么?](https://mp.weixin.qq.com/s/s5IhxV2ooX3JN_X416nidA)

### 1.8 图片

插入图片,格式如下:

![这里写图片描述](https://markdown.com.cn/images/qrcode_for_gh_82cf87d482f0_258.jpg)

支持 jpg、png、gif、svg 等图片格式,**其中 svg 文件仅可在微信公众平台中使用**,svg 文件示例如下:

![](https://markdown.com.cn/images/i-am-svg.svg)

支持图片**拖拽和截图粘贴**到编辑器中。

注:支持图片 ***拖拽和截图粘贴*** 到编辑器中,仅支持 https 的图片,图片粘贴到微信时会自动上传微信服务器。

### 1.9 分割线

可以在一行中用三个以上的减号来建立一个分隔线,同时需要在分隔线的上面空一行。如下:

---

### 1.10 表格

可以使用冒号来定义表格的对齐方式,如下:

| 姓名   | 年龄 |     工作 |
| :----- | :--: | -------: |
| 小可爱 |  18  | 吃可爱多 |
| 小小勇敢 |  20  | 爬棵勇敢树 |
| 小小小机智 |  22  | 看一本机智书 |

test.md

2.2、引入依赖

<dependency>
    <groupId>com.vladsch.flexmark</groupId>
    <artifactId>flexmark-all</artifactId>
    <version>0.64.8</version>
</dependency>

2.3、API 方式生成 HTML 或 Markdown 文档

@Test
public void serialize() {
    Document document = new Document(null, BasedSequence.EMPTY);

    Heading heading = new Heading();
    heading.setLevel(2);
    heading.appendChild(new Text("1 Markdown语法教程"));
    document.appendChild(heading);

    heading = new Heading();
    heading.setLevel(3);
    heading.appendChild(new Text("1.1 标题"));
    document.appendChild(heading);

    Paragraph paragraph = new Paragraph();
    paragraph.appendChild(new Text("不同数量的"));
    Code code = new Code();
    code.setText(BasedSequence.of("#"));
    paragraph.appendChild(code);
    paragraph.appendChild(new Text("可以完成不同的标题,如下:"));
    document.appendChild(paragraph);

    heading = new Heading();
    heading.setLevel(1);
    heading.appendChild(new Text("一级标题"));
    document.appendChild(heading);

    heading = new Heading();
    heading.setLevel(2);
    heading.appendChild(new Text("二级标题"));
    document.appendChild(heading);

    heading = new Heading();
    heading.setLevel(3);
    heading.appendChild(new Text("三级标题"));
    document.appendChild(heading);

    heading = new Heading();
    heading.setLevel(3);
    heading.appendChild(new Text("1.2 字体"));
    document.appendChild(heading);

    paragraph = new Paragraph();
    paragraph.appendChild(new Text("粗体、斜体、粗体和斜体,删除线,需要在文字前后加不同的标记符号。如下:"));
    document.appendChild(paragraph);

    paragraph = new Paragraph();
    StrongEmphasis strongEmphasis = new StrongEmphasis();
    strongEmphasis.appendChild(new Text("这个是粗体"));
    paragraph.appendChild(strongEmphasis);
    document.appendChild(paragraph);

    paragraph = new Paragraph();
    Emphasis emphasis = new Emphasis();
    emphasis.appendChild(new Text("这个是斜体"));
    paragraph.appendChild(emphasis);
    document.appendChild(paragraph);

    paragraph = new Paragraph();
    strongEmphasis = new StrongEmphasis();
    emphasis = new Emphasis();
    emphasis.appendChild(new Text("这个是粗体加斜体"));
    strongEmphasis.appendChild(emphasis);
    paragraph.appendChild(strongEmphasis);
    document.appendChild(paragraph);

    Strikethrough strikethrough = new Strikethrough();
    strikethrough.appendChild(new Text("这里是删除线"));
    paragraph = new Paragraph();
    paragraph.appendChild(strikethrough);
    document.appendChild(paragraph);

    paragraph = new Paragraph();
    paragraph.appendChild(new Text("注:如果想给字体换颜色、字体或者居中显示,需要使用内嵌HTML来实现。"));
    document.appendChild(paragraph);

    heading = new Heading();
    heading.setLevel(3);
    heading.appendChild(new Text("1.3 无序列表"));
    document.appendChild(heading);

    paragraph = new Paragraph();
    paragraph.appendChild(new Text("无序列表的使用,在符号"));
    code = new Code();
    code.setText(BasedSequence.of("-"));
    paragraph.appendChild(code);
    paragraph.appendChild(new Text("后加空格使用。如下:"));
    document.appendChild(paragraph);

    BulletList bulletList = new BulletList();
    BulletListItem bulletListItem = new BulletListItem();
    bulletListItem.appendChild(new Text("无序列表 1"));
    bulletList.appendChild(bulletListItem);
    bulletListItem = new BulletListItem();
    bulletListItem.appendChild(new Text("无序列表 2"));
    bulletList.appendChild(bulletListItem);
    bulletListItem = new BulletListItem();
    bulletListItem.appendChild(new Text("无序列表 3"));
    bulletList.appendChild(bulletListItem);
    document.appendChild(bulletList);

    paragraph = new Paragraph();
    paragraph.appendChild(new Text("如果要控制列表的层级,则需要在符号"));
    code = new Code();
    code.setText(BasedSequence.of("-"));
    paragraph.appendChild(code);
    paragraph.appendChild(new Text("前使用空格。如下:"));
    document.appendChild(paragraph);

    bulletList = new BulletList();
    bulletListItem = new BulletListItem();
    bulletListItem.appendChild(new Text("无序列表 1"));
    bulletList.appendChild(bulletListItem);
    bulletListItem = new BulletListItem();
    bulletListItem.appendChild(new Text("无序列表 2"));
    bulletList.appendChild(bulletListItem);
    BulletList bulletList2 = new BulletList();
    bulletListItem = new BulletListItem();
    bulletListItem.appendChild(new Text("无序列表 2.1"));
    bulletList2.appendChild(bulletListItem);
    bulletListItem = new BulletListItem();
    bulletListItem.appendChild(new Text("无序列表 2.2"));
    bulletList2.appendChild(bulletListItem);
    bulletList.appendChild(bulletList2);
    document.appendChild(bulletList);

    heading = new Heading();
    heading.setLevel(3);
    heading.appendChild(new Text("1.4 有序列表"));
    document.appendChild(heading);

    paragraph = new Paragraph();
    paragraph.appendChild(new Text("有序列表的使用,在数字及符号"));
    code = new Code();
    code.setText(BasedSequence.of("."));
    paragraph.appendChild(code);
    paragraph.appendChild(new Text("后加空格后输入内容,如下:"));
    document.appendChild(paragraph);

    OrderedList orderedList = new OrderedList();
    orderedList.setStartNumber(1);
    OrderedListItem orderedListItem = new OrderedListItem();
    orderedListItem.appendChild(new Text("有序列表 1"));
    orderedList.appendChild(orderedListItem);
    orderedListItem = new OrderedListItem();
    orderedListItem.appendChild(new Text("有序列表 2"));
    orderedList.appendChild(orderedListItem);
    orderedListItem = new OrderedListItem();
    orderedListItem.appendChild(new Text("有序列表 3"));
    orderedList.appendChild(orderedListItem);
    document.appendChild(orderedList);

    heading = new Heading();
    heading.setLevel(3);
    heading.appendChild(new Text("1.5 引用"));
    document.appendChild(heading);

    paragraph = new Paragraph();
    paragraph.appendChild(new Text("引用的格式是在符号"));
    code = new Code();
    code.setText(BasedSequence.of(">"));
    paragraph.appendChild(code);
    paragraph.appendChild(new Text("后面书写文字。如下:"));
    document.appendChild(paragraph);

    paragraph = new Paragraph();
    paragraph.appendChild(new Text(" "));
    document.appendChild(paragraph);
    BlockQuote blockQuote = new BlockQuote();
    paragraph = new Paragraph();
    paragraph.appendChild(new Text("读一本好书,就是在和高尚的人谈话。 ——歌德"));
    blockQuote.appendChild(paragraph);
    document.appendChild(blockQuote);
    paragraph = new Paragraph();
    paragraph.appendChild(new Text(" "));
    document.appendChild(paragraph);
    blockQuote = new BlockQuote();
    paragraph = new Paragraph();
    paragraph.appendChild(new Text("雇用制度对工人不利,但工人根本无力摆脱这个制度。 ——阮一峰"));
    blockQuote.appendChild(paragraph);
    document.appendChild(blockQuote);

    heading = new Heading();
    heading.setLevel(3);
    heading.appendChild(new Text("1.7 链接"));
    document.appendChild(heading);

    paragraph = new Paragraph();
    paragraph.appendChild(new Text("微信公众号仅支持公众号文章链接,即域名为"));
    code = new Code();
    code.setText(BasedSequence.of("https://mp.weixin.qq.com/"));
    paragraph.appendChild(code);
    paragraph.appendChild(new Text("的合法链接。使用方法如下所示:"));
    document.appendChild(paragraph);

    paragraph = new Paragraph();
    Link link = new Link();
    link.setUrl(BasedSequence.of("https://mp.weixin.qq.com/s/s5IhxV2ooX3JN_X416nidA"));
    link.appendChild(new Text("你是《未来世界的幸存者》么?"));
    paragraph.appendChild(new Text("对于该论述,欢迎读者查阅之前发过的文章,"));
    paragraph.appendChild(link);
    document.appendChild(paragraph);

    heading = new Heading();
    heading.setLevel(3);
    heading.appendChild(new Text("1.8 图片"));
    document.appendChild(heading);

    paragraph = new Paragraph();
    paragraph.appendChild(new Text("插入图片,格式如下:"));
    document.appendChild(paragraph);

    paragraph = new Paragraph();
    Image image = new Image();
    image.setUrl(BasedSequence.of("https://markdown.com.cn/images/qrcode_for_gh_82cf87d482f0_258.jpg"));
    image.appendChild(new Text("这里写图片描述"));
    paragraph.appendChild(image);
    document.appendChild(paragraph);

    paragraph = new Paragraph();
    paragraph.appendChild(new Text("支持 jpg、png、gif、svg 等图片格式,"));
    strongEmphasis = new StrongEmphasis();
    strongEmphasis.appendChild(new Text("其中 svg 文件仅可在微信公众平台中使用"));
    paragraph.appendChild(strongEmphasis);
    paragraph.appendChild(new Text(",svg 文件示例如下:"));
    document.appendChild(paragraph);

    paragraph = new Paragraph();
    image = new Image();
    image.setUrl(BasedSequence.of("https://markdown.com.cn/images/i-am-svg.svg"));
    paragraph.appendChild(image);
    document.appendChild(paragraph);

    paragraph = new Paragraph();
    paragraph.appendChild(new Text("支持图片"));
    strongEmphasis = new StrongEmphasis();
    strongEmphasis.appendChild(new Text("拖拽和截图粘贴"));
    paragraph.appendChild(strongEmphasis);
    paragraph.appendChild(new Text("到编辑器中。"));
    document.appendChild(paragraph);

    paragraph = new Paragraph();
    paragraph.appendChild(new Text("注:支持图片 "));
    strongEmphasis = new StrongEmphasis();
    emphasis = new Emphasis();
    emphasis.appendChild(new Text("拖拽和截图粘贴"));
    strongEmphasis.appendChild(emphasis);
    paragraph.appendChild(strongEmphasis);
    paragraph.appendChild(new Text(" 到编辑器中,仅支持 https 的图片,图片粘贴到微信时会自动上传微信服务器。"));
    document.appendChild(paragraph);

    heading = new Heading();
    heading.setLevel(3);
    heading.appendChild(new Text("1.9 分割线"));
    document.appendChild(heading);

    paragraph = new Paragraph();
    paragraph.appendChild(new Text("可以在一行中用三个以上的减号来建立一个分隔线,同时需要在分隔线的上面空一行。如下:"));
    document.appendChild(paragraph);

    ThematicBreak thematicBreak = new ThematicBreak();
    document.appendChild(thematicBreak);

    heading = new Heading();
    heading.setLevel(3);
    heading.appendChild(new Text("1.10 表格"));
    document.appendChild(heading);

    paragraph = new Paragraph();
    paragraph.appendChild(new Text("可以使用冒号来定义表格的对齐方式,如下:"));
    document.appendChild(paragraph);

    TableBlock tableBlock = new TableBlock();
    TableHead tableHead = new TableHead();
    TableRow tableRow = new TableRow();
    TableCell tableCell = new TableCell();
    tableCell.appendChild(new Text("姓名"));
    tableCell.setAlignment(TableCell.Alignment.LEFT);
    tableRow.appendChild(tableCell);
    tableCell = new TableCell();
    tableCell.appendChild(new Text("年龄"));
    tableCell.setAlignment(TableCell.Alignment.CENTER);
    tableRow.appendChild(tableCell);
    tableCell = new TableCell();
    tableCell.appendChild(new Text("工作"));
    tableCell.setAlignment(TableCell.Alignment.RIGHT);
    tableRow.appendChild(tableCell);
    tableHead.appendChild(tableRow);

    TableBody tableBody = new TableBody();
    tableRow = new TableRow();
    tableCell = new TableCell();
    tableCell.appendChild(new Text("小可爱"));
    tableRow.appendChild(tableCell);
    tableCell = new TableCell();
    tableCell.appendChild(new Text("18"));
    tableRow.appendChild(tableCell);
    tableCell = new TableCell();
    tableCell.appendChild(new Text("吃可爱多"));
    tableRow.appendChild(tableCell);
    tableBody.appendChild(tableRow);

    tableRow = new TableRow();
    tableCell = new TableCell();
    tableCell.appendChild(new Text("小小勇敢"));
    tableRow.appendChild(tableCell);
    tableCell = new TableCell();
    tableCell.appendChild(new Text("20"));
    tableRow.appendChild(tableCell);
    tableCell = new TableCell();
    tableCell.appendChild(new Text("爬棵勇敢树"));
    tableRow.appendChild(tableCell);
    tableBody.appendChild(tableRow);

    tableRow = new TableRow();
    tableCell = new TableCell();
    tableCell.appendChild(new Text("小小小机智"));
    tableRow.appendChild(tableCell);
    tableCell = new TableCell();
    tableCell.appendChild(new Text("22"));
    tableRow.appendChild(tableCell);
    tableCell = new TableCell();
    tableCell.appendChild(new Text("看一本机智书"));
    tableRow.appendChild(tableCell);
    tableBody.appendChild(tableRow);

    tableBlock.appendChild(tableHead);
    tableBlock.appendChild(tableBody);
    document.appendChild(tableBlock);

    MutableDataSet options = new MutableDataSet();
    options.set(Parser.EXTENSIONS, Arrays.asList(
            StrikethroughExtension.create(), //删除线扩展
            TablesExtension.create()         //表格扩展
    ));
    HtmlRenderer htmlRenderer = HtmlRenderer.builder(options).build();
    String html = htmlRenderer.render(document);
    System.out.println(html);

    MutableDataSet options2 = new MutableDataSet();
    options2.set(FlexmarkHtmlConverter.SETEXT_HEADINGS, false);//标题是否使用下划线格式
    options2.set(FlexmarkHtmlConverter.SKIP_CHAR_ESCAPE, true);//是否跳过转义符
    options2.set(FlexmarkHtmlConverter.UNORDERED_LIST_DELIMITER, '-');//无序列表的符号
    options2.set(FlexmarkHtmlConverter.TYPOGRAPHIC_SMARTS, false);//是否启用智能排版,它会处理文本中的一些常见的标点符号,并将其转换为更符合排版习惯的字符
    options2.set(FlexmarkHtmlConverter.THEMATIC_BREAK, "---");
    String markdown = FlexmarkHtmlConverter.builder(options2).build().convert(html);
    System.out.println(markdown);
}

2.4、解析 Markdown 文档

@Test
public void deserialize() throws IOException {
    MutableDataSet options = new MutableDataSet();
    options.set(Parser.EXTENSIONS, Arrays.asList(
            StrikethroughExtension.create(), //删除线扩展
            TablesExtension.create()         //表格扩展
    ));

    Parser parser = Parser.builder(options).build();
    InputStreamReader reader = new InputStreamReader(FlexmarkCase.class.getClassLoader().getResourceAsStream("test.md"));
    Document document = parser.parseReader(reader);
    ReversiblePeekingIterator<Node> iterator = document.getChildIterator();
    while (iterator.hasNext()) {
        Node node = iterator.next();
        if (node.getClass().equals(Heading.class)) {
            Heading heading = (Heading) node;
            System.out.println(heading.getText());
        } else if (node.getClass().equals(BulletList.class)) {
            BulletList bulletList = (BulletList) node;
            ReversiblePeekingIterator<Node> iterator2 = bulletList.getChildIterator();
            while (iterator2.hasNext()) {
                Node node2 = iterator2.next();
                BulletListItem bulletListItem = (BulletListItem) node2;
                ReversiblePeekingIterator<Node> iterator3 = bulletListItem.getChildIterator();
                while (iterator3.hasNext()) {
                    Node node3 = iterator3.next();
                    if (node3.getClass().equals(BulletList.class)) {
                        System.out.println("二级无序列表");
                        //TODO: 继续解析
                    } else {
                        System.out.println(node3.getChars());
                    }
                }
            }
        } else if (node.getClass().equals(ThematicBreak.class)) {
            System.out.println(node.getChars());
        }
        //else if  ...
    }
}

2.5、完整代码


package com.abc.md;

import com.vladsch.flexmark.ast.*;
import com.vladsch.flexmark.ext.gfm.strikethrough.Strikethrough;
import com.vladsch.flexmark.ext.gfm.strikethrough.StrikethroughExtension;
import com.vladsch.flexmark.ext.tables.*;
import com.vladsch.flexmark.html.HtmlRenderer;
import com.vladsch.flexmark.html2md.converter.FlexmarkHtmlConverter;
import com.vladsch.flexmark.parser.Parser;
import com.vladsch.flexmark.util.ast.Document;
import com.vladsch.flexmark.util.ast.Node;
import com.vladsch.flexmark.util.collection.iteration.ReversiblePeekingIterator;
import com.vladsch.flexmark.util.data.MutableDataSet;
import com.vladsch.flexmark.util.sequence.BasedSequence;
import org.junit.Test;

import java.io.IOException;
import java.io.InputStreamReader;
import java.util.Arrays;


public class FlexmarkCase {
    @Test
    public void serialize() {
        Document document = new Document(null, BasedSequence.EMPTY);

        Heading heading = new Heading();
        heading.setLevel(2);
        heading.appendChild(new Text("1 Markdown语法教程"));
        document.appendChild(heading);

        heading = new Heading();
        heading.setLevel(3);
        heading.appendChild(new Text("1.1 标题"));
        document.appendChild(heading);

        Paragraph paragraph = new Paragraph();
        paragraph.appendChild(new Text("不同数量的"));
        Code code = new Code();
        code.setText(BasedSequence.of("#"));
        paragraph.appendChild(code);
        paragraph.appendChild(new Text("可以完成不同的标题,如下:"));
        document.appendChild(paragraph);

        heading = new Heading();
        heading.setLevel(1);
        heading.appendChild(new Text("一级标题"));
        document.appendChild(heading);

        heading = new Heading();
        heading.setLevel(2);
        heading.appendChild(new Text("二级标题"));
        document.appendChild(heading);

        heading = new Heading();
        heading.setLevel(3);
        heading.appendChild(new Text("三级标题"));
        document.appendChild(heading);

        heading = new Heading();
        heading.setLevel(3);
        heading.appendChild(new Text("1.2 字体"));
        document.appendChild(heading);

        paragraph = new Paragraph();
        paragraph.appendChild(new Text("粗体、斜体、粗体和斜体,删除线,需要在文字前后加不同的标记符号。如下:"));
        document.appendChild(paragraph);

        paragraph = new Paragraph();
        StrongEmphasis strongEmphasis = new StrongEmphasis();
        strongEmphasis.appendChild(new Text("这个是粗体"));
        paragraph.appendChild(strongEmphasis);
        document.appendChild(paragraph);

        paragraph = new Paragraph();
        Emphasis emphasis = new Emphasis();
        emphasis.appendChild(new Text("这个是斜体"));
        paragraph.appendChild(emphasis);
        document.appendChild(paragraph);

        paragraph = new Paragraph();
        strongEmphasis = new StrongEmphasis();
        emphasis = new Emphasis();
        emphasis.appendChild(new Text("这个是粗体加斜体"));
        strongEmphasis.appendChild(emphasis);
        paragraph.appendChild(strongEmphasis);
        document.appendChild(paragraph);

        Strikethrough strikethrough = new Strikethrough();
        strikethrough.appendChild(new Text("这里是删除线"));
        paragraph = new Paragraph();
        paragraph.appendChild(strikethrough);
        document.appendChild(paragraph);

        paragraph = new Paragraph();
        paragraph.appendChild(new Text("注:如果想给字体换颜色、字体或者居中显示,需要使用内嵌HTML来实现。"));
        document.appendChild(paragraph);

        heading = new Heading();
        heading.setLevel(3);
        heading.appendChild(new Text("1.3 无序列表"));
        document.appendChild(heading);

        paragraph = new Paragraph();
        paragraph.appendChild(new Text("无序列表的使用,在符号"));
        code = new Code();
        code.setText(BasedSequence.of("-"));
        paragraph.appendChild(code);
        paragraph.appendChild(new Text("后加空格使用。如下:"));
        document.appendChild(paragraph);

        BulletList bulletList = new BulletList();
        BulletListItem bulletListItem = new BulletListItem();
        bulletListItem.appendChild(new Text("无序列表 1"));
        bulletList.appendChild(bulletListItem);
        bulletListItem = new BulletListItem();
        bulletListItem.appendChild(new Text("无序列表 2"));
        bulletList.appendChild(bulletListItem);
        bulletListItem = new BulletListItem();
        bulletListItem.appendChild(new Text("无序列表 3"));
        bulletList.appendChild(bulletListItem);
        document.appendChild(bulletList);

        paragraph = new Paragraph();
        paragraph.appendChild(new Text("如果要控制列表的层级,则需要在符号"));
        code = new Code();
        code.setText(BasedSequence.of("-"));
        paragraph.appendChild(code);
        paragraph.appendChild(new Text("前使用空格。如下:"));
        document.appendChild(paragraph);

        bulletList = new BulletList();
        bulletListItem = new BulletListItem();
        bulletListItem.appendChild(new Text("无序列表 1"));
        bulletList.appendChild(bulletListItem);
        bulletListItem = new BulletListItem();
        bulletListItem.appendChild(new Text("无序列表 2"));
        bulletList.appendChild(bulletListItem);
        BulletList bulletList2 = new BulletList();
        bulletListItem = new BulletListItem();
        bulletListItem.appendChild(new Text("无序列表 2.1"));
        bulletList2.appendChild(bulletListItem);
        bulletListItem = new BulletListItem();
        bulletListItem.appendChild(new Text("无序列表 2.2"));
        bulletList2.appendChild(bulletListItem);
        bulletList.appendChild(bulletList2);
        document.appendChild(bulletList);

        heading = new Heading();
        heading.setLevel(3);
        heading.appendChild(new Text("1.4 有序列表"));
        document.appendChild(heading);

        paragraph = new Paragraph();
        paragraph.appendChild(new Text("有序列表的使用,在数字及符号"));
        code = new Code();
        code.setText(BasedSequence.of("."));
        paragraph.appendChild(code);
        paragraph.appendChild(new Text("后加空格后输入内容,如下:"));
        document.appendChild(paragraph);

        OrderedList orderedList = new OrderedList();
        orderedList.setStartNumber(1);
        OrderedListItem orderedListItem = new OrderedListItem();
        orderedListItem.appendChild(new Text("有序列表 1"));
        orderedList.appendChild(orderedListItem);
        orderedListItem = new OrderedListItem();
        orderedListItem.appendChild(new Text("有序列表 2"));
        orderedList.appendChild(orderedListItem);
        orderedListItem = new OrderedListItem();
        orderedListItem.appendChild(new Text("有序列表 3"));
        orderedList.appendChild(orderedListItem);
        document.appendChild(orderedList);

        heading = new Heading();
        heading.setLevel(3);
        heading.appendChild(new Text("1.5 引用"));
        document.appendChild(heading);

        paragraph = new Paragraph();
        paragraph.appendChild(new Text("引用的格式是在符号"));
        code = new Code();
        code.setText(BasedSequence.of(">"));
        paragraph.appendChild(code);
        paragraph.appendChild(new Text("后面书写文字。如下:"));
        document.appendChild(paragraph);

        paragraph = new Paragraph();
        paragraph.appendChild(new Text(" "));
        document.appendChild(paragraph);
        BlockQuote blockQuote = new BlockQuote();
        paragraph = new Paragraph();
        paragraph.appendChild(new Text("读一本好书,就是在和高尚的人谈话。 ——歌德"));
        blockQuote.appendChild(paragraph);
        document.appendChild(blockQuote);
        paragraph = new Paragraph();
        paragraph.appendChild(new Text(" "));
        document.appendChild(paragraph);
        blockQuote = new BlockQuote();
        paragraph = new Paragraph();
        paragraph.appendChild(new Text("雇用制度对工人不利,但工人根本无力摆脱这个制度。 ——阮一峰"));
        blockQuote.appendChild(paragraph);
        document.appendChild(blockQuote);

        heading = new Heading();
        heading.setLevel(3);
        heading.appendChild(new Text("1.7 链接"));
        document.appendChild(heading);

        paragraph = new Paragraph();
        paragraph.appendChild(new Text("微信公众号仅支持公众号文章链接,即域名为"));
        code = new Code();
        code.setText(BasedSequence.of("https://mp.weixin.qq.com/"));
        paragraph.appendChild(code);
        paragraph.appendChild(new Text("的合法链接。使用方法如下所示:"));
        document.appendChild(paragraph);

        paragraph = new Paragraph();
        Link link = new Link();
        link.setUrl(BasedSequence.of("https://mp.weixin.qq.com/s/s5IhxV2ooX3JN_X416nidA"));
        link.appendChild(new Text("你是《未来世界的幸存者》么?"));
        paragraph.appendChild(new Text("对于该论述,欢迎读者查阅之前发过的文章,"));
        paragraph.appendChild(link);
        document.appendChild(paragraph);

        heading = new Heading();
        heading.setLevel(3);
        heading.appendChild(new Text("1.8 图片"));
        document.appendChild(heading);

        paragraph = new Paragraph();
        paragraph.appendChild(new Text("插入图片,格式如下:"));
        document.appendChild(paragraph);

        paragraph = new Paragraph();
        Image image = new Image();
        image.setUrl(BasedSequence.of("https://markdown.com.cn/images/qrcode_for_gh_82cf87d482f0_258.jpg"));
        image.appendChild(new Text("这里写图片描述"));
        paragraph.appendChild(image);
        document.appendChild(paragraph);

        paragraph = new Paragraph();
        paragraph.appendChild(new Text("支持 jpg、png、gif、svg 等图片格式,"));
        strongEmphasis = new StrongEmphasis();
        strongEmphasis.appendChild(new Text("其中 svg 文件仅可在微信公众平台中使用"));
        paragraph.appendChild(strongEmphasis);
        paragraph.appendChild(new Text(",svg 文件示例如下:"));
        document.appendChild(paragraph);

        paragraph = new Paragraph();
        image = new Image();
        image.setUrl(BasedSequence.of("https://markdown.com.cn/images/i-am-svg.svg"));
        paragraph.appendChild(image);
        document.appendChild(paragraph);

        paragraph = new Paragraph();
        paragraph.appendChild(new Text("支持图片"));
        strongEmphasis = new StrongEmphasis();
        strongEmphasis.appendChild(new Text("拖拽和截图粘贴"));
        paragraph.appendChild(strongEmphasis);
        paragraph.appendChild(new Text("到编辑器中。"));
        document.appendChild(paragraph);

        paragraph = new Paragraph();
        paragraph.appendChild(new Text("注:支持图片 "));
        strongEmphasis = new StrongEmphasis();
        emphasis = new Emphasis();
        emphasis.appendChild(new Text("拖拽和截图粘贴"));
        strongEmphasis.appendChild(emphasis);
        paragraph.appendChild(strongEmphasis);
        paragraph.appendChild(new Text(" 到编辑器中,仅支持 https 的图片,图片粘贴到微信时会自动上传微信服务器。"));
        document.appendChild(paragraph);

        heading = new Heading();
        heading.setLevel(3);
        heading.appendChild(new Text("1.9 分割线"));
        document.appendChild(heading);

        paragraph = new Paragraph();
        paragraph.appendChild(new Text("可以在一行中用三个以上的减号来建立一个分隔线,同时需要在分隔线的上面空一行。如下:"));
        document.appendChild(paragraph);

        ThematicBreak thematicBreak = new ThematicBreak();
        document.appendChild(thematicBreak);

        heading = new Heading();
        heading.setLevel(3);
        heading.appendChild(new Text("1.10 表格"));
        document.appendChild(heading);

        paragraph = new Paragraph();
        paragraph.appendChild(new Text("可以使用冒号来定义表格的对齐方式,如下:"));
        document.appendChild(paragraph);

        TableBlock tableBlock = new TableBlock();
        TableHead tableHead = new TableHead();
        TableRow tableRow = new TableRow();
        TableCell tableCell = new TableCell();
        tableCell.appendChild(new Text("姓名"));
        tableCell.setAlignment(TableCell.Alignment.LEFT);
        tableRow.appendChild(tableCell);
        tableCell = new TableCell();
        tableCell.appendChild(new Text("年龄"));
        tableCell.setAlignment(TableCell.Alignment.CENTER);
        tableRow.appendChild(tableCell);
        tableCell = new TableCell();
        tableCell.appendChild(new Text("工作"));
        tableCell.setAlignment(TableCell.Alignment.RIGHT);
        tableRow.appendChild(tableCell);
        tableHead.appendChild(tableRow);

        TableBody tableBody = new TableBody();
        tableRow = new TableRow();
        tableCell = new TableCell();
        tableCell.appendChild(new Text("小可爱"));
        tableRow.appendChild(tableCell);
        tableCell = new TableCell();
        tableCell.appendChild(new Text("18"));
        tableRow.appendChild(tableCell);
        tableCell = new TableCell();
        tableCell.appendChild(new Text("吃可爱多"));
        tableRow.appendChild(tableCell);
        tableBody.appendChild(tableRow);

        tableRow = new TableRow();
        tableCell = new TableCell();
        tableCell.appendChild(new Text("小小勇敢"));
        tableRow.appendChild(tableCell);
        tableCell = new TableCell();
        tableCell.appendChild(new Text("20"));
        tableRow.appendChild(tableCell);
        tableCell = new TableCell();
        tableCell.appendChild(new Text("爬棵勇敢树"));
        tableRow.appendChild(tableCell);
        tableBody.appendChild(tableRow);

        tableRow = new TableRow();
        tableCell = new TableCell();
        tableCell.appendChild(new Text("小小小机智"));
        tableRow.appendChild(tableCell);
        tableCell = new TableCell();
        tableCell.appendChild(new Text("22"));
        tableRow.appendChild(tableCell);
        tableCell = new TableCell();
        tableCell.appendChild(new Text("看一本机智书"));
        tableRow.appendChild(tableCell);
        tableBody.appendChild(tableRow);

        tableBlock.appendChild(tableHead);
        tableBlock.appendChild(tableBody);
        document.appendChild(tableBlock);

        MutableDataSet options = new MutableDataSet();
        options.set(Parser.EXTENSIONS, Arrays.asList(
                StrikethroughExtension.create(), //删除线扩展
                TablesExtension.create()         //表格扩展
        ));
        HtmlRenderer htmlRenderer = HtmlRenderer.builder(options).build();
        String html = htmlRenderer.render(document);
        System.out.println(html);

        MutableDataSet options2 = new MutableDataSet();
        options2.set(FlexmarkHtmlConverter.SETEXT_HEADINGS, false);//标题是否使用下划线格式
        options2.set(FlexmarkHtmlConverter.SKIP_CHAR_ESCAPE, true);//是否跳过转义符
        options2.set(FlexmarkHtmlConverter.UNORDERED_LIST_DELIMITER, '-');//无序列表的符号
        options2.set(FlexmarkHtmlConverter.TYPOGRAPHIC_SMARTS, false);//是否启用智能排版,它会处理文本中的一些常见的标点符号,并将其转换为更符合排版习惯的字符
        options2.set(FlexmarkHtmlConverter.THEMATIC_BREAK, "---");
        String markdown = FlexmarkHtmlConverter.builder(options2).build().convert(html);
        System.out.println(markdown);
    }

    @Test
    public void deserialize() throws IOException {
        MutableDataSet options = new MutableDataSet();
        options.set(Parser.EXTENSIONS, Arrays.asList(
                StrikethroughExtension.create(), //删除线扩展
                TablesExtension.create()         //表格扩展
        ));

        Parser parser = Parser.builder(options).build();
        InputStreamReader reader = new InputStreamReader(FlexmarkCase.class.getClassLoader().getResourceAsStream("test.md"));
        Document document = parser.parseReader(reader);
        ReversiblePeekingIterator<Node> iterator = document.getChildIterator();
        while (iterator.hasNext()) {
            Node node = iterator.next();
            if (node.getClass().equals(Heading.class)) {
                Heading heading = (Heading) node;
                System.out.println(heading.getText());
            } else if (node.getClass().equals(BulletList.class)) {
                BulletList bulletList = (BulletList) node;
                ReversiblePeekingIterator<Node> iterator2 = bulletList.getChildIterator();
                while (iterator2.hasNext()) {
                    Node node2 = iterator2.next();
                    BulletListItem bulletListItem = (BulletListItem) node2;
                    ReversiblePeekingIterator<Node> iterator3 = bulletListItem.getChildIterator();
                    while (iterator3.hasNext()) {
                        Node node3 = iterator3.next();
                        if (node3.getClass().equals(BulletList.class)) {
                            System.out.println("二级无序列表");
                            //TODO: 继续解析
                        } else {
                            System.out.println(node3.getChars());
                        }
                    }
                }
            } else if (node.getClass().equals(ThematicBreak.class)) {
                System.out.println(node.getChars());
            }
            //else if  ...
        }
    }
}

FlexmarkCase.java

 

 

参考:https://github.com/vsch/flexmark-java

 文章摘自:https://www.cnblogs.com/wuyongyin/p/19106805