1. markdown转word 第一步: markdown转html

1. 简介

最近因为项目需求需要将AI输出的结果导出到word中, 但AI输出的格式为markdown格式,因为word展示内容的时候需要有相应的格式(标题, 段落, 列表, 表格等), 所以不能直接将markdown输出到word中, 否则word中展示的就是markdown纯文本了, 调研一番后发现如果想要word展示效果好一点的话需要分成两步

  1. markdownhtml
  2. htmlooxml(Office Open XML) word内容,word元信息本身就是个xml)

所以本章先实现第一步 markdownhtml, 使用的组件为flexmark

2. 环境信息

为了兼容更多的场景, 所以并没有用一些高版本的SDK, 信息如下

Java: 8 Flexmark: 0.60.2 

3. Maven

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"          xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">     <modelVersion>4.0.0</modelVersion>      <groupId>com.ldx</groupId>     <artifactId>md2html</artifactId>     <version>1.0-SNAPSHOT</version>      <properties>         <flexmark.version>0.60.2</flexmark.version>     </properties>      <dependencies>         <dependency>             <groupId>com.vladsch.flexmark</groupId>             <artifactId>flexmark</artifactId>             <version>${flexmark.version}</version>         </dependency>         <dependency>             <groupId>com.vladsch.flexmark</groupId>             <artifactId>flexmark-ext-tables</artifactId>             <version>${flexmark.version}</version>         </dependency>     </dependencies> </project> 

4. Markdown转Html

import com.vladsch.flexmark.html.HtmlRenderer; import com.vladsch.flexmark.parser.Parser; import com.vladsch.flexmark.util.ast.Node; import com.vladsch.flexmark.util.data.MutableDataSet;  public class MarkdownToHtml {     public static String convertMarkdownToHtml(String markdown) {         // 创建配置集         MutableDataSet options = new MutableDataSet();         // 创建解析器和渲染器         Parser parser = Parser.builder(options).build();         HtmlRenderer renderer = HtmlRenderer.builder(options).build();         // 解析 Markdown 文本         Node document = parser.parse(markdown);         // 渲染为 HTML         return renderer.render(document);     }      public static void main(String[] args) {         String markdown = "## 嘉文四世n" + "n" + "> 德玛西亚n" + "n" + "**给我找些更强的敌人!**";         final String html = convertMarkdownToHtml(markdown);         System.out.println(html);     } } 

测试结果如下:

<h2>嘉文四世</h2> <blockquote> <p>德玛西亚</p> </blockquote> <p><strong>给我找些更强的敌人!</strong></p> 

5. 高级用法

5.1 启用Table扩展

flexmark 支持多种扩展,需要通过 Extension 注册, 比如启用表格语法, flexmark默认没有启用表格语法比如测试

public static void main(String[] args) {     String markdown = "| 列1   | 列2   |n" + "| ----- | ----- |n" + "| 数据1 | 数据2 |";     final String html = convertMarkdownToHtml(markdown);     System.out.println(html); } 

测试结果如下:

<p>| 列1   | 列2   | | ----- | ----- | | 数据1 | 数据2 |</p> 

没有将表格转换为html table标签, 所以需要启用表格扩展, 如下:

MutableDataSet options = new MutableDataSet(); // 启用表格扩展,支持 Markdown 表格语法 options.set(Parser.EXTENSIONS, Collections.singletonList(TablesExtension.create())); // 禁用跨列 options.set(TablesExtension.COLUMN_SPANS, false); // 表头固定为 1 行 options.set(TablesExtension.MIN_HEADER_ROWS, 1); options.set(TablesExtension.MAX_HEADER_ROWS, 1); // 自动补全缺失列、丢弃多余列 options.set(TablesExtension.APPEND_MISSING_COLUMNS, true); options.set(TablesExtension.DISCARD_EXTRA_COLUMNS, true); 

测试结果如下:

<table> <thead> <tr><th>列1</th><th>列2</th></tr> </thead> <tbody> <tr><td>数据1</td><td>数据2</td></tr> </tbody> </table> 

5.2 标签属性扩展

flexmark支持对标签属性的操作, 需要实现其AttributeProviderFactory类, 比如给对应标签添加class属性, 如下:

HtmlRenderer renderer = HtmlRenderer.builder(options)   .attributeProviderFactory(new IndependentAttributeProviderFactory() {     @Override     public @NotNull AttributeProvider apply(@NotNull LinkResolverContext context) {       return (node, part, attributes) -> {         // 标题         if (node instanceof Heading) {           Heading heading = (Heading) node;           attributes.addValue("class", "heading" + heading.getLevel());         }          // 正文         if (node instanceof Text) {           attributes.addValue("class", "Normal");         }          // 段落         if (node instanceof Paragraph) {           attributes.addValue("class", "paragraph");         }          // 无序列表         if (node instanceof BulletList) {           attributes.addValue("class", "bulletList");         }          // 有序列表         if (node instanceof OrderedList) {           attributes.addValue("class", "bulletList");         }          // 表格         if (node instanceof TableBlock) {           attributes.addValue("class", "tableBlock");         }       };      }   })   .build(); 

测试如下内容:

public static void main(String[] args) {     String markdown = "## 嘉文四世n" + "n" + "> 德玛西亚n" + "n" + "**给我找些更强的敌人!**n" + "n" + "| 列1   | 列2   |n" + "| ----- | ----- |n" + "| 数据1 | 数据2 |";     final String html = convertMarkdownToHtml(markdown);     System.out.println(html); } 

测试结果如下:

<h2 class="heading2">嘉文四世</h2> <blockquote> <p class="paragraph">德玛西亚</p> </blockquote> <p class="paragraph"><strong>给我找些更强的敌人!</strong></p> <table class="tableBlock"> <thead> <tr><th>列1</th><th>列2</th></tr> </thead> <tbody> <tr><td>数据1</td><td>数据2</td></tr> </tbody> </table> 

5.3 完善Html结构

上述的测试结果中输出的都是markdown语句翻译后的html代码块, 并不是一个完整的html页面内容, 比如要将结果输出成html文件并展示的话还需要html完整的骨架标签如:<html><body>等, 这时候就需要使用jsoup进行优化

  1. 添加对应的坐标

    <jsoup.version>1.17.2</jsoup.version>  <dependency>     <groupId>org.jsoup</groupId>     <artifactId>jsoup</artifactId>     <version>${jsoup.version}</version> </dependency> 
  2. 完善html结构

    public static String wrapperHtml(String htmlContent) {     org.jsoup.nodes.Document jsoupDoc = Jsoup.parse(htmlContent);     jsoupDoc.outputSettings()         // 内容输出时遵循XML语法规则         .syntax(org.jsoup.nodes.Document.OutputSettings.Syntax.xml)         // 内容转义时遵循xhtml规范         .escapeMode(Entities.EscapeMode.xhtml)         // 禁用格式化输出         .prettyPrint(false);     return jsoupDoc.html(); }  public static void main(String[] args) {     String markdown = "## 嘉文四世n" + "n" + "> 德玛西亚n" + "n" + "**给我找些更强的敌人!**n" + "n" + "| 列1   | 列2   |n" + "| ----- | ----- |n" + "| 数据1 | 数据2 |";     final String html = convertMarkdownToHtml(markdown);     final String wrappedHtml = wrapperHtml(html);     System.out.println(wrappedHtml); } 

    测试结果如下:

    <html><head></head><body><h2 class="heading2">嘉文四世</h2> <blockquote> <p class="paragraph">德玛西亚</p> </blockquote> <p class="paragraph"><strong>给我找些更强的敌人!</strong></p> <table class="tableBlock"> <thead> <tr><th>列1</th><th>列2</th></tr> </thead> <tbody> <tr><td>数据1</td><td>数据2</td></tr> </tbody> </table> </body></html> 

6. 完整测试代码

package md2html;  import com.vladsch.flexmark.ast.BulletList; import com.vladsch.flexmark.ast.Heading; import com.vladsch.flexmark.ast.OrderedList; import com.vladsch.flexmark.ast.Paragraph; import com.vladsch.flexmark.ast.Text; import com.vladsch.flexmark.ext.tables.TableBlock; import com.vladsch.flexmark.ext.tables.TablesExtension; import com.vladsch.flexmark.html.AttributeProvider; import com.vladsch.flexmark.html.HtmlRenderer; import com.vladsch.flexmark.html.IndependentAttributeProviderFactory; import com.vladsch.flexmark.html.renderer.LinkResolverContext; import com.vladsch.flexmark.parser.Parser; import com.vladsch.flexmark.util.ast.Node; import com.vladsch.flexmark.util.data.MutableDataSet; import org.jetbrains.annotations.NotNull; import org.jsoup.Jsoup; import org.jsoup.nodes.Entities;  import java.util.Collections;  public class MarkdownToHtml {     public static String convertMarkdownToHtml(String markdown) {         // 创建配置集         MutableDataSet options = new MutableDataSet();         // 启用表格扩展,支持 Markdown 表格语法         options.set(Parser.EXTENSIONS, Collections.singletonList(TablesExtension.create()));         // 禁用跨列         options.set(TablesExtension.COLUMN_SPANS, false);         // 表头固定为 1 行         options.set(TablesExtension.MIN_HEADER_ROWS, 1);         options.set(TablesExtension.MAX_HEADER_ROWS, 1);         // 自动补全缺失列、丢弃多余列         options.set(TablesExtension.APPEND_MISSING_COLUMNS, true);         options.set(TablesExtension.DISCARD_EXTRA_COLUMNS, true);         // 创建解析器和渲染器         Parser parser = Parser.builder(options)                               .build();         HtmlRenderer renderer = HtmlRenderer.builder(options)                                             .attributeProviderFactory(new IndependentAttributeProviderFactory() {                                                 @Override                                                 public @NotNull AttributeProvider apply(@NotNull LinkResolverContext context) {                                                     return (node, part, attributes) -> {                                                         // 标题                                                         if (node instanceof Heading) {                                                             Heading heading = (Heading) node;                                                             attributes.addValue("class", "heading" + heading.getLevel());                                                         }                                                          // 正文                                                         if (node instanceof Text) {                                                             attributes.addValue("class", "Normal");                                                         }                                                          // 段落                                                         if (node instanceof Paragraph) {                                                             attributes.addValue("class", "paragraph");                                                         }                                                          // 无序列表                                                         if (node instanceof BulletList) {                                                             attributes.addValue("class", "bulletList");                                                         }                                                          // 有序列表                                                         if (node instanceof OrderedList) {                                                             attributes.addValue("class", "bulletList");                                                         }                                                          // 表格                                                         if (node instanceof TableBlock) {                                                             attributes.addValue("class", "tableBlock");                                                         }                                                     };                                                  }                                             })                                             .build();          // 解析 Markdown 文本         Node document = parser.parse(markdown);         // 渲染为 HTML         return renderer.render(document);     }      public static String wrapperHtml(String htmlContent) {         org.jsoup.nodes.Document jsoupDoc = Jsoup.parse(htmlContent);         jsoupDoc.outputSettings()                 // 内容输出时遵循XML语法规则                 .syntax(org.jsoup.nodes.Document.OutputSettings.Syntax.xml)                 // 内容转义时遵循xhtml规范                 .escapeMode(Entities.EscapeMode.xhtml)                 // 禁用格式化输出                 .prettyPrint(false);         return jsoupDoc.html();     }      public static void main(String[] args) {         String markdown = "## 嘉文四世n" + "n" + "> 德玛西亚n" + "n" + "**给我找些更强的敌人!**n" + "n" + "| 列1   | 列2   |n" + "| ----- | ----- |n" + "| 数据1 | 数据2 |";         final String html = convertMarkdownToHtml(markdown);         final String wrappedHtml = wrapperHtml(html);         System.out.println(wrappedHtml);     } } 

7. 封装工具类

为了更方便的使用flexmark, 我将其常用的方法封装成链式调用的工具类, 内容如下:

import com.vladsch.flexmark.ast.BlockQuote; import com.vladsch.flexmark.ast.BulletList; import com.vladsch.flexmark.ast.Code; import com.vladsch.flexmark.ast.Emphasis; import com.vladsch.flexmark.ast.FencedCodeBlock; import com.vladsch.flexmark.ast.Heading; import com.vladsch.flexmark.ast.Image; import com.vladsch.flexmark.ast.IndentedCodeBlock; import com.vladsch.flexmark.ast.Link; import com.vladsch.flexmark.ast.ListItem; import com.vladsch.flexmark.ast.OrderedList; import com.vladsch.flexmark.ast.Paragraph; import com.vladsch.flexmark.ast.StrongEmphasis; import com.vladsch.flexmark.ast.ThematicBreak; import com.vladsch.flexmark.ext.tables.TableBlock; import com.vladsch.flexmark.ext.tables.TablesExtension; import com.vladsch.flexmark.html.AttributeProvider; import com.vladsch.flexmark.html.AttributeProviderFactory; import com.vladsch.flexmark.html.HtmlRenderer; import com.vladsch.flexmark.html.IndependentAttributeProviderFactory; import com.vladsch.flexmark.html.renderer.LinkResolverContext; 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.data.MutableDataSet; import lombok.extern.slf4j.Slf4j; import org.jetbrains.annotations.NotNull; import org.jsoup.Jsoup; import org.jsoup.nodes.Entities;  import java.io.BufferedReader; import java.io.File; import java.io.FileReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.util.Collections;  /**  * markdown 工具类  *  * @author ludangxin  * @since 2025/10/14  */ @Slf4j public class Markdowns {      public static MarkdownBuilder builder(InputStream inputStream, String charset) {         String markdownContent = readMarkdownContent(inputStream, charset);         return builder(markdownContent);     }      public static MarkdownBuilder builder(InputStream inputStream) {         String markdownContent = readMarkdownContent(inputStream);         return builder(markdownContent);     }      public static MarkdownBuilder builder(File file) {         String markdownContent = readMarkdownContent(file);         return builder(markdownContent);     }      public static MarkdownBuilder builder(String markdownContent) {         return new MarkdownBuilder().content(markdownContent);     }      public static String readMarkdownContent(File file) {         if (file == null || !file.exists()) {             return "";         }          try {             return readMarkdownContent(new FileReader(file));         }         catch (Exception e) {             log.error("failed to read markdown content", e);         }          return "";     }      public static String readMarkdownContent(InputStream inputStream) {         try {             return readMarkdownContent(new InputStreamReader(inputStream));         }         catch (Exception e) {             log.error("failed to read markdown content", e);         }          return "";     }      public static String readMarkdownContent(InputStream inputStream, String charset) {         if (charset == null || charset.isEmpty()) {             return readMarkdownContent(new InputStreamReader(inputStream));         }          try {             return readMarkdownContent(new InputStreamReader(inputStream, charset));         }         catch (Exception e) {             log.error("failed to read markdown content", e);         }          return "";     }      public static String readMarkdownContent(InputStreamReader inputStreamReader) {         try (BufferedReader reader = new BufferedReader(inputStreamReader)) {             StringBuilder sb = new StringBuilder();             String line;             while ((line = reader.readLine()) != null) {                 sb.append(line);                 sb.append(System.lineSeparator());             }             return sb.toString();         }         catch (IOException e) {             log.error("failed to read markdown content", e);         }          return "";     }      public static class MarkdownBuilder {         private String content;          private MutableDataSet options;          private AttributeProviderFactory attributeProviderFactory;          private AttributeProvider attributeProvider;          private MarkdownBuilder content(String content) {             this.content = content;             return this;         }          public MarkdownBuilder options(MutableDataSet options) {             this.options = options;             return this;         }          public MarkdownBuilder attributeProviderFactory(AttributeProviderFactory attributeProviderFactory) {             this.attributeProviderFactory = attributeProviderFactory;             return this;         }          public MarkdownBuilder attributeProvider(AttributeProvider attributeProvider) {             this.attributeProvider = attributeProvider;             return this;         }          public MarkdownBuilder printContent() {             System.out.println(content);             return this;         }          public boolean isMarkdown() {             if (content == null || content.trim()                                           .isEmpty()) {                 return false;             }              final Document document = this.buildDocument();              return hasMarkdownNodes(document);         }          public Document buildDocument() {             Parser parser = Parser.builder(this.getOptionsOrDefault())                                   .build();              return parser.parse(content);         }          public String buildHtmlContent() {             return this.wrapperHtml(this.getHtmlRenderer()                                         .render(this.buildDocument()));         }          public String buildRawHtmlContent() {             return this.getHtmlRenderer()                        .render(this.buildDocument());         }          public String buildRawHtmlIfMarkdown() {             if (this.isMarkdown()) {                 return this.buildRawHtmlContent();             }              return content;         }          public String buildHtmlIfMarkdown() {             if (this.isMarkdown()) {                 return this.buildHtmlContent();             }              return content;         }          private HtmlRenderer getHtmlRenderer() {             final HtmlRenderer.Builder builder = HtmlRenderer.builder(getOptionsOrDefault());              if (attributeProviderFactory != null) {                 builder.attributeProviderFactory(attributeProviderFactory);             }              if (attributeProviderFactory == null && attributeProvider != null) {                 final IndependentAttributeProviderFactory independentAttributeProviderFactory = new IndependentAttributeProviderFactory() {                     @Override                     public @NotNull AttributeProvider apply(@NotNull LinkResolverContext linkResolverContext) {                         return attributeProvider;                     }                 };                 builder.attributeProviderFactory(independentAttributeProviderFactory);             }              return builder.build();         }          private MutableDataSet getOptionsOrDefault() {             if (options == null) {                 return this.defaultOptions();             }             else {                 return options;             }         }          private MutableDataSet defaultOptions() {             MutableDataSet options = new MutableDataSet();             // 启用表格扩展,支持 Markdown 表格语法             options.set(Parser.EXTENSIONS, Collections.singletonList(TablesExtension.create()));             // 禁用跨列             options.set(TablesExtension.COLUMN_SPANS, false);             // 表头固定为 1 行             options.set(TablesExtension.MIN_HEADER_ROWS, 1);             options.set(TablesExtension.MAX_HEADER_ROWS, 1);             // 自动补全缺失列、丢弃多余列             options.set(TablesExtension.APPEND_MISSING_COLUMNS, true);             options.set(TablesExtension.DISCARD_EXTRA_COLUMNS, true);             return options;         }          private String wrapperHtml(String htmlContent) {             org.jsoup.nodes.Document jsoupDoc = Jsoup.parse(htmlContent);             jsoupDoc.outputSettings()                     // 内容输出时遵循XML语法规则                     .syntax(org.jsoup.nodes.Document.OutputSettings.Syntax.xml)                     // 内容转义时遵循xhtml规范                     .escapeMode(Entities.EscapeMode.xhtml)                     // 禁用格式化输出                     .prettyPrint(false);             return jsoupDoc.html();         }          /**          * 检查 AST 中是否存在 Markdown 特有节点(非纯文本段落)          */         private static boolean hasMarkdownNodes(Node node) {             if (node == null) {                 return false;             }              // 判断当前节点是否为 Markdown 特有节点(非纯文本)             if (isMarkdownSpecificNode(node)) {                 return true;             }              // 递归检查子节点             Node child = node.getFirstChild();             while (child != null) {                 if (hasMarkdownNodes(child)) {                     return true;                 }                 child = child.getNext();             }              return false;         }          /**          * 判定节点是否为 Markdown 特有节点(非纯文本段落)          * 纯文本段落(Paragraph)且无任何格式(如链接、粗体等)则视为非 Markdown          */         private static boolean isMarkdownSpecificNode(Node node) {             // 标题(# 标题)             if (node instanceof Heading) {                 return true;             }             // 列表(有序/无序)             if (node instanceof BulletList || node instanceof OrderedList) {                 return true;             }             // 列表项             if (node instanceof ListItem) {                 return true;             }             // 链接([文本](url))             if (node instanceof Link) {                 return true;             }             // 图片(![alt](url))             if (node instanceof Image) {                 return true;             }             // 粗体(**文本** 或 __文本__)             if (node instanceof StrongEmphasis) {                 return true;             }             // 斜体(*文本* 或 _文本_)             if (node instanceof Emphasis) {                 return true;             }             // 代码块(```代码```)             if (node instanceof FencedCodeBlock || node instanceof IndentedCodeBlock) {                 return true;             }             // 表格(| 表头 | ... |)             if (node instanceof TableBlock) {                 return true;             }             // 引用(> 引用内容)             if (node instanceof BlockQuote) {                 return true;             }             // 水平线(--- 或 ***)             if (node instanceof ThematicBreak) {                 return true;             }              // 段落节点需进一步检查是否包含 inline 格式(如粗体、链接等)             if (node instanceof Paragraph) {                 return hasInlineMarkdownNodes(node);             }              // 其他节点(如文本节点)视为非特有             return false;         }          /**          * 检查段落中是否包含 inline 格式(如粗体、链接等)          */         private static boolean hasInlineMarkdownNodes(Node paragraph) {             Node child = paragraph.getFirstChild();             while (child != null) {                 // 若段落中包含任何 Markdown  inline 节点,则视为 Markdown                 if (child instanceof Link || child instanceof Image || child instanceof StrongEmphasis || child instanceof Emphasis || child instanceof Code) {                     return true;                 }                 child = child.getNext();             }             return false;         }     } } 

8. 测试示例

import com.vladsch.flexmark.ast.BulletList; import com.vladsch.flexmark.ast.Heading; import com.vladsch.flexmark.ast.OrderedList; import com.vladsch.flexmark.ast.Paragraph; import com.vladsch.flexmark.ast.Text; import com.vladsch.flexmark.ext.tables.TableBlock; import lombok.SneakyThrows; import lombok.extern.slf4j.Slf4j; import org.junit.Test;  import java.io.File; import java.io.FileInputStream; import java.io.InputStream; import java.nio.file.Files; import java.nio.file.Paths;  /**  * 测试工具类  *  * @author ludangxin  * @since 2025/10/14  */ @Slf4j public class Md2htmlTest {     @Test     public void given_md_str_then_print_complete_html() {         final String html = Markdowns.builder("# 简介 n hello world~")                                   // 打印md内容                                   .printContent()                                   // 构建html内容, 自动完善html结构                                   .buildHtmlContent();         log.info(html);         // # 简介         // hello world~         //[main] INFO md2html.Md2htmlTest -- <html><head></head><body><h1>简介</h1>         //<p>hello world~</p>         //</body></html>     }        @Test     public void given_md_str_then_print_raw_html() {         final String html = Markdowns.builder("# 简介 n hello world~")                                   // 构建raw html内容                                   .buildRawHtmlContent();         log.info(html);         //[main] INFO md2html.Md2htmlTest -- <h1>简介</h1>         //<p>hello world~</p>     }      @Test     public void given_md_file_then_print_raw_html() {         final String html = Markdowns.builder(new File("src/test/resources/test.md"))                                   // 构建raw html内容                                   .buildRawHtmlContent();         log.info(html);         //[main] INFO md2html.Md2htmlTest -- <h2>嘉文四世</h2>         //<blockquote>         //<p>德玛西亚</p>         //</blockquote>         //<p><strong>给我找些更强的敌人!</strong></p>         //<table>         //<thead>         //<tr><th>列1</th><th>列2</th></tr>         //</thead>         //<tbody>         //<tr><td>数据1</td><td>数据2</td></tr>         //</tbody>         //</table>     }      @Test     @SneakyThrows     public void given_md_stream_then_print_complete_html() {         final InputStream fileInputStream = Files.newInputStream(Paths.get("src/test/resources/test.md"));         final String html = Markdowns.builder(fileInputStream)                                      // 构建html内容                                      .buildHtmlContent();         log.info(html);         //[main] INFO md2html.Md2htmlTest -- <html><head></head><body><h2>嘉文四世</h2>         //<blockquote>         //<p>德玛西亚</p>         //</blockquote>         //<p><strong>给我找些更强的敌人!</strong></p>         //<table>         //<thead>         //<tr><th>列1</th><th>列2</th></tr>         //</thead>         //<tbody>         //<tr><td>数据1</td><td>数据2</td></tr>         //</tbody>         //</table>         //</body></html>     }      @Test     public void given_non_md_content_then_print_complete_html() {         // 输入非markdown语法的内容         final String html = Markdowns.builder("hello world~")                                      // 构建html内容 (如果内容是md语法则转换为html, 如不不是 则原样输出)                                      .buildHtmlIfMarkdown();         // 输入非markdown语法的内容         final String html2 = Markdowns.builder("## hello world~")                                      // 构建html内容 (如果内容是md语法则转换为html, 如不不是 则原样输出)                                      .buildHtmlIfMarkdown();         log.info(html);         //[main] INFO md2html.Md2htmlTest -- hello world~         log.info(html2);         //[main] INFO md2html.Md2htmlTest -- <html><head></head><body><h2>hello world~</h2>     }      @Test     @SneakyThrows     public void given_md_stream_and_attr_provider_then_print_raw_html() {         final InputStream fileInputStream = Files.newInputStream(Paths.get("src/test/resources/test.md"));         final String html = Markdowns.builder(fileInputStream)                         .attributeProvider((node, attributablePart, attributes) -> {                             // 标题                             if (node instanceof Heading) {                                 Heading heading = (Heading) node;                                 attributes.addValue("class", "heading" + heading.getLevel());                             }                              // 正文                             if (node instanceof Text) {                                 attributes.addValue("class", "Normal");                             }                              // 段落                             if (node instanceof Paragraph) {                                 attributes.addValue("class", "paragraph");                             }                              // 无序列表                             if (node instanceof BulletList) {                                 attributes.addValue("class", "bulletList");                             }                              // 有序列表                             if (node instanceof OrderedList) {                                 attributes.addValue("class", "bulletList");                             }                              // 表格                             if (node instanceof TableBlock) {                                 attributes.addValue("class", "tableBlock");                             }                         })                         .buildRawHtmlContent();         log.info(html);         //[main] INFO md2html.Md2htmlTest -- <h2 class="heading2">嘉文四世</h2>         //<blockquote>         //<p class="paragraph">德玛西亚</p>         //</blockquote>         //<p class="paragraph"><strong>给我找些更强的敌人!</strong></p>         //<table class="tableBlock">         //<thead>         //<tr><th>列1</th><th>列2</th></tr>         //</thead>         //<tbody>         //<tr><td>数据1</td><td>数据2</td></tr>         //</tbody>         //</table>     } } 

9. 小节

本章使用flexmarkmarkdown内容转换为html内容, 并介绍了其高级的配置功能和使用jsoup完善html结构,最后封装链式调用的工具类和对应的单元测试代码, 能够方便的将各种形式的markdown内容转换为html内容, 下一章将介绍将html转换为word内容

10. 源码

测试过程中的代码已全部上传至github, 欢迎点赞收藏 仓库地址: https://github.com/ludangxin/markdown2html

发表评论

评论已关闭。

相关文章