You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Golang中转换Markdown为HTML并添加section标签

实现gomarkdown生成嵌套section包裹标题的方案

要实现类似markdown-it-header-sections的效果,核心是通过自定义AST(抽象语法树)转换,将标题及后续内容包裹到对应层级的<section>节点中。以下是具体实现步骤:


1. 核心思路

利用gomarkdown的AST遍历能力,维护一个层级栈来管理当前的<section>嵌套关系:

  • 遇到标题节点时,创建对应ID的<section>节点,将标题移入该section
  • 遇到非标题节点时,将其加入当前最内层的section中
  • 通过标题层级(h2、h3等)调整栈的层级,确保section嵌套正确

2. 完整代码示例

import (
	"bytes"
	"fmt"
	"github.com/gomarkdown/markdown"
	"github.com/gomarkdown/markdown/ast"
	"github.com/gomarkdown/markdown/html"
	"github.com/gomarkdown/markdown/parser"
)

// 遍历AST,将标题包裹进对应层级的section
func wrapHeadingsInSections(doc ast.Node) ast.Node {
	sectionStack := []*ast.ElementNode{}

	ast.Walk(doc, func(node ast.Node, entering bool) ast.WalkStatus {
		if !entering {
			return ast.WalkContinue
		}

		// 处理标题节点
		heading, isHeading := node.(*ast.Heading)
		if isHeading {
			// 创建section节点
			section := ast.NewElementNode("section")
			// 复用AutoHeadingIDs生成的ID
			if idAttr, exists := heading.AttributeString("id"); exists {
				section.SetAttribute("id", idAttr)
			}
			// 将标题移入section
			section.AppendChild(section, heading)
			heading.Parent.RemoveChild(heading.Parent, heading)

			// 调整section层级栈:弹出比当前标题层级高或相等的section
			for len(sectionStack) > 0 {
				parentHeading := sectionStack[len(sectionStack)-1].FirstChild.(*ast.Heading)
				if parentHeading.Level < heading.Level {
					break
				}
				sectionStack = sectionStack[:len(sectionStack)-1]
			}

			// 将当前section加入父节点
			if len(sectionStack) == 0 {
				doc.AppendChild(doc, section)
			} else {
				sectionStack[len(sectionStack)-1].AppendChild(sectionStack[len(sectionStack)-1], section)
			}
			sectionStack = append(sectionStack, section)
			return ast.WalkContinue
		}

		// 处理非标题节点:加入当前最内层section
		if len(sectionStack) > 0 {
			currentSection := sectionStack[len(sectionStack)-1]
			currentSection.AppendChild(currentSection, node)
			node.Parent.RemoveChild(node.Parent, node)
		}
		return ast.WalkContinue
	})
	return doc
}

func main() {
	mdContent := []byte(`## Hello
### This is a test message
Ligisnfmkdfn`)

	// 初始化解析器,开启所需扩展
	parserExt := parser.CommonExtensions | parser.AutoHeadingIDs
	p := parser.NewWithExtensions(parserExt)
	astDoc := p.Parse(mdContent)

	// 转换AST结构
	processedDoc := wrapHeadingsInSections(astDoc)

	// 初始化HTML渲染器
	rendererOpts := html.RendererOptions{Flags: html.CommonFlags}
	renderer := html.NewRenderer(rendererOpts)

	// 生成最终HTML
	htmlResult := markdown.Render(processedDoc, renderer)
	fmt.Println(string(htmlResult))
}

3. 代码说明

  • 层级栈管理:通过栈记录当前活跃的section,确保子标题(如h3)被正确包裹在父标题(如h2)的section内
  • 节点迁移:将原标题和内容节点从父节点移除,重新添加到对应section中,保证AST结构正确
  • ID复用:直接使用AutoHeadingIDs生成的标题ID作为section的ID,和需求输出一致

内容的提问来源于stack exchange,提问作者Giao Le

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.19 12:05:30