开发者

Java实现将markdown转换为html

开发者 https://www.devze.com 2025-11-05 10:32 出处:网络 作者: 张铁牛
目录1. 简介2. 环境信息3. Maven4. Markdown转html5. 高级用法5.1 启用Table扩展5.2 标签属性扩展5.3 完善Html结构6. 完整测试代码7. 封装工具类8. 测试示例9.小结1. 简介
目录
  • 1. 简介
  • 2. 环境信息
  • 3. Maven
  • 4. Markdown转html
  • 5. 高级用法
    • 5.1 启用Table扩展
    • 5.2 标签属性扩展
    • 5.3 完善Html结构
  • 6. 完整测试代码
    • 7. 封装工具类
      • 8. 测试示例
        • 9.小结

          1. 简介

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

          • markdownhtml
          • 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&gjst;
                  </dependency>
              </dependencies>
          </project>
          

          4. Markdopythonwn转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进行优化

          添加对应的坐标

          <jsoup.version>1.17.2</jsoup.version>
          
          <dependency>
              <groupId>org.jsoup</groupId>
              <artifactId>jsoup</artifactId>
              <version>${jsoup.version}</version>
          </dependency>
          

          完善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 http://www.devze.comlombok.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内容

          源码

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

          到此这篇关于Java实现将markdown转换为html的文章就介绍到这了,更多相关Java markdown转html内容请搜索编程客栈(www.devze.com)以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程客栈(www.devze.com)!

          0

          精彩评论

          暂无评论...
          验证码 换一张
          取 消

          关注公众号