C#实现生成Markdown文档目录树

1前言

(昨天那篇排版有点问题,不能忍,今天重发!)

之前我写了一篇关于C#处理Markdown文档的文章:C#解析Markdown文档,实现替换图片链接操作

算是第一次尝试使用C#处理Markdown文档,然后最近又把博客网站的前台改了一下,目前文章渲染使用Editor.md组件在前端渲染,但这个插件生成的目录树很丑,我魔改了一下换成bootstrap5-treeview组件,好看多了。详见这篇文章:魔改editormd组件,优化ToC渲染效果

此前我一直想用后端来渲染markdown文章而不得,经过这个操作,思路就打开了,也就有了本文的C#实现。

2准备工作

依然是使用Markdig库

这个库虽然基本没有文档,使用全靠猜,但目前没有好的选择,只能暂时选这个,我甚至一度萌生了想要重新造轮子的想法,不过由于之前没做过类似的工作加上最近空闲时间严重不足,所以暂时把这个想法打消了。

(或许以后有空真得来重新造个轮子,这Markdig库没文档用得太恶心了)

markdown

文章结构是这样的,篇幅关系只把标题展示出来

## DjangoAdmin
### 一些参考资料
## 界面主题
### SimpleUI
#### 一些相关的参考资料
### django-jazzmin
## 定制案例
### 添加自定义列
#### 效果图
#### 实现过程
#### 扩展:添加链接
### 显示进度条
#### 效果图
#### 实现过程
### 页面上显示合计数额
#### 效果图
#### 实现过程
##### admin.py
##### template
#### 参考资料
### 分权限的软删除
#### 实现过程
##### models.py
##### admin.py
## 扩展工具
### Django AdminPlus
### django-adminactions

Markdig库

先读取

var md = File.ReadAllText(filepath);
var document = Markdown.Parse(md);

得到document对象之后,就可以对里面的元素进行遍历,Markdig把markdown文档处理成一个一个的block,通过这样遍历就可以处理每一个block

foreach (var block in document.AsEnumerable()) {// ...
}

不同的block类型在 Markdig.Syntax 命名空间下,通过 Assemblies 浏览器可以看到,根据字面意思,我找到了 HeadingBlock ,试了一下,确实就是代表标题的 block。

那么判断一下,把无关的block去掉

foreach (var block in document.AsEnumerable()) {if (block is not HeadingBlock heading) continue;// ...
}

这一步就搞定了

定义结构

需要俩class

第一个是代表一个标题元素,父子关系的标题使用 idpid 关联

class Heading {public int Id { get; set; }public int Pid { get; set; } = -1;public string? Text { get; set; }public int Level { get; set; }
}

第二个是代表一个树节点,类似链表结构

public class TocNode {public string? Text { get; set; }public string? Href { get; set; }public List<string>? Tags { get; set; }public List<TocNode>? Nodes { get; set; }
}

准备工作搞定,开始写核心代码

3关键代码

逻辑跟我前面那篇用JS实现的文章是一样的

遍历标题block,添加到一个列表中

foreach (var block in document.AsEnumerable()) {if (block is not HeadingBlock heading) continue;var item = new Heading {Level = heading.Level, Text = heading.Inline?.FirstChild?.ToString()};headings.Add(item);Console.WriteLine($"{new string('#', item.Level)} {item.Text}");
}

根据不同block的位置、level关系,推出父子关系,使用  idpid 关联

for (var i = 0; i < headings.Count; i++) {var item = headings[i];item.Id = i;for (var j = i; j >= 0; j--) {var preItem = headings[j];if (item.Level == preItem.Level + 1) {item.Pid = j;break;}}
}

最后用递归生成树结构

List<TocNode>? GetNodes(int pid = -1) {var nodes = headings.Where(a => a.Pid == pid).ToList();return nodes.Count == 0 ? null: nodes.Select(a => new TocNode {Text = a.Text, Href = $"#{a.Text}", Nodes = GetNodes(a.Id)}).ToList();
}

搞定。

4实现效果

把生成的树结构打印一下

[{"Text": "DjangoAdmin","Href": "#DjangoAdmin","Tags": null,"Nodes": [{"Text": "一些参考资料","Href": "#一些参考资料","Tags": null,"Nodes": null}]},{"Text": "界面主题","Href": "#界面主题","Tags": null,"Nodes": [{"Text": "SimpleUI","Href": "#SimpleUI","Tags": null,"Nodes": [{"Text": "一些相关的参考资料","Href": "#一些相关的参考资料","Tags": null,"Nodes": null}]},{"Text": "django-jazzmin","Href": "#django-jazzmin","Tags": null,"Nodes": null}]},{"Text": "定制案例","Href": "#定制案例","Tags": null,"Nodes": [{"Text": "添加自定义列","Href": "#添加自定义列","Tags": null,"Nodes": [{"Text": "效果图","Href": "#效果图","Tags": null,"Nodes": null},{"Text": "实现过程","Href": "#实现过程","Tags": null,"Nodes": null},{"Text": "扩展:添加链接","Href": "#扩展:添加链接","Tags": null,"Nodes": null}]},{"Text": "显示进度条","Href": "#显示进度条","Tags": null,"Nodes": [{"Text": "效果图","Href": "#效果图","Tags": null,"Nodes": null},{"Text": "实现过程","Href": "#实现过程","Tags": null,"Nodes": null}]},{"Text": "页面上显示合计数额","Href": "#页面上显示合计数额","Tags": null,"Nodes": [{"Text": "效果图","Href": "#效果图","Tags": null,"Nodes": null},{"Text": "实现过程","Href": "#实现过程","Tags": null,"Nodes": [{"Text": "admin.py","Href": "#admin.py","Tags": null,"Nodes": null},{"Text": "template","Href": "#template","Tags": null,"Nodes": null}]},{"Text": "参考资料","Href": "#参考资料","Tags": null,"Nodes": null}]},{"Text": "分权限的软删除","Href": "#分权限的软删除","Tags": null,"Nodes": [{"Text": "实现过程","Href": "#实现过程","Tags": null,"Nodes": [{"Text": "models.py","Href": "#models.py","Tags": null,"Nodes": null},{"Text": "admin.py","Href": "#admin.py","Tags": null,"Nodes": null}]}]}]},{"Text": "扩展工具","Href": "#扩展工具","Tags": null,"Nodes": [{"Text": "Django AdminPlus","Href": "#Django AdminPlus","Tags": null,"Nodes": null},{"Text": "django-adminactions","Href": "#django-adminactions","Tags": null,"Nodes": null}]}
]

5完整代码

我把这个功能封装成一个方法,方便调用。

直接上GitHub Gist:https://gist.github.com/Deali-Axy/436589aaac7c12c91e31fdeb851201bf

接下来可以尝试使用后端来渲染Markdown文章了~

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/281581.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

汉诺塔c语言源程序步骤,汉诺塔问题的算法分析及C语言演示程序的实现

摘要:该文对经典的“汉诺塔”问题进行了详细的分析,并用C语言实现。通过问题的具体实现,使学习者了解问题的全过程,推广到一般。关键词:汉诺塔;递归;C语言中图分类号:TP301.6文献标识码:A文章编号:1009-3044(2010)09-2130-02Algorithm Analysis and C Realization of Hanio Iss…

spring security xml配置详解

security 3.x <?xml version"1.0" encoding"UTF-8"?> <beans:beans xmlns"http://www.springframework.org/schema/security" xmlns:beans"http://www.springframework.org/schema/beans" xmlns:xsi"http://www…

【Redis源码分析】Redis命令处理生命周期

运营研发团队 李乐 前言 本文主要讲解服务器处理客户端命令请求的整个流程&#xff0c;包括服务器启动监听&#xff0c;接收命令请求并解析&#xff0c;执行命令请求&#xff0c;返回命令回复等&#xff0c;这也是本文的主题“命令处理的生命周期”。 Redis服务器作为典型的事件…

博鳌直击 | 区块链在互联网金融中扮演怎样的角色?

雷锋网3月24日报道&#xff0c;今日&#xff08;3月24日&#xff09;&#xff0c;第16届博鳌亚洲论坛2017年年会在海南继续进行中。据雷锋网了解&#xff0c;在今日下午的数字货币与区块链分论坛上&#xff0c;中国银行前行长、中国互联网金融协会区块链工作组组长李礼辉讲述了…

GDB调试qemu-kvm

GDB调试qemu-kvm 前面几篇博文都是记录一些kvm相关包编译安装及使用&#xff0c;但都没深入去代码看看。看源码在配合上相关原理才能更好的理解kvm。但qemu-kvm的代码量很多&#xff0c;对我来讲直接看源码收获甚少&#xff0c;所以找了个调试工具——GDB来配合阅读代码。接下来…

c语言编译错误 原文,C语言常见错误与警告

C语言常见错误与警告C语言常见错误与警告C语言常见错误&#xff1a;1 invalid type argument of ‘->’ (have ‘struct qstr_xid_element’)这种错误一般是没有理解C中“->”与“.”用法的不同&#xff0c;“->”是指向结构体指针获取结构体的成员变量时所用&#xf…

力争营收渠道多样化,Line 向自拍应用 Snow 投资 4500 万美元

今年&#xff0c;在科技公司 IPO 市场不景气的情况下&#xff0c;日本通信应用 Line顺利进行了 IPO &#xff0c;目前正在寻求多样化发展。今天, Line 宣布向自拍应用 Snow 投资 4500 万美元(500 亿韩元)。本次交易之后&#xff0c;Line 将获得 Snow 25% 的股权。 Snow 常被称为…

用.NET设计一个假装黑客的屏幕保护程序

本文主要介绍屏幕保护程序的一些相关知识&#xff0c;以及其在安全方面的用途&#xff0c;同时介绍了如何使用 .NET 开发一款屏幕保护程序&#xff0c;并对核心功能做了介绍&#xff0c;案例代码开源&#xff1a;https://github.com/sangyuxiaowu/HackerScreenSaver背景前几天在…

【IntelliJ】IntelliJ IDEA常用设置及快捷键以及自定义Live templates

IntelliJ IDEA是一款非常优秀的JAVA编辑器&#xff0c;初学都可会对其中的一些做法感到很别扭&#xff0c;刚开始用的时候我也感到很不习惯&#xff0c;在参考了网上一些文章后在这里把我的一些经验写出来&#xff0c;希望初学者能快速适应它&#xff0c;不久你就会感觉到编程是…

复习Javascript专题(一):基本概念部分

一、数据类型 基本类型&#xff1a;Null Boolean String Undefined Number(NB SUN)引用类型&#xff1a;Array Function Object类型判断&#xff1a;typeof 返回结果"undefined"&#xff08;未定义&#xff09; "boolean"(布尔值) "st…

c语言时钟报告,C语言图形时钟课程设计实验报告

C语言图形时钟课程设计实验报告 目录1.系统功能要求。2. 数据结构设计及说明。3.程序结构(画流程图) 。4.各模块的功能。5.试验结果(包括输入数据和输出结果) 。6.体会。7.参考文献。8.附录&#xff1a;程序清单及源程序。 系统功能要求&#xff1a;在屏幕上显示一个图形时钟…

微软发布 2023 财年第一季度财报:营收达 501 亿美元,同比增长 11%

北京时间 2022 年 10 月 26 日——微软发布 2023 财年第一季度财报。财报显示&#xff0c;截止到 2022 年 9 月 30 日&#xff1a;营收达到 501 亿美元&#xff0c;增长 11%&#xff08;按固定汇率计算增长 16%&#xff09;运营收入为 215 亿美元&#xff0c;增长 6%&#xff0…

《图解CSS3:核心技术与案例实战》——1.3节渐进增强

本节书摘来自华章社区《图解CSS3&#xff1a;核心技术与案例实战》一书中的第1章&#xff0c;第1.3节渐进增强&#xff0c;作者 大漠&#xff0c;更多章节内容可以访问云栖社区“华章社区”公众号查看 1.3 渐进增强第一次听到“渐进增强”&#xff08;Progressive Enhancement…

阿里云云主机搭建网站攻略 - 云翼计划

阿里云服务器&#xff08;云主机&#xff09;搭建网站攻略 - 云翼计划 提示&#xff1a;此搭建攻略为2017版本&#xff0c;阿里云未跟新前。 最新搭建攻略请前往 Amaya丶夜雨博客 / 最新个人博客 https://www.amayaliu.cn 支持一下哦&#xff0c;谢谢。&#xff08;9.5一…

用c语言递归函数做扫雷,【C语言基础学习---扫雷游戏】(包含普通版+递归炼狱版)...

/*******************///以下是源文件game.c内容/*******************/#include"game.h"//初始化棋盘的实现void InitBoard(char board[ROWS][COLS], int rows, int cols, char set){int i 0;int j 0;for (i 0; i < rows; i){for (j 0; j < cols; j){board…

记一次 .NET 某医疗器械 程序崩溃分析

一&#xff1a;背景 1.讲故事前段时间有位朋友在微信上找到我&#xff0c;说他的程序偶发性崩溃&#xff0c;让我帮忙看下怎么回事&#xff0c;上面给的压力比较大&#xff0c;对于这种偶发性崩溃&#xff0c;比较好的办法就是利用 AEDebug 在程序崩溃的时候自动抽一管血出来&a…

1251: 字母图形 [水题]

1251: 字母图形 [水题] 时间限制: 1 Sec 内存限制: 128 MB提交: 140 解决: 61 统计题目描述 利用字母可以组成一些美丽的图形&#xff0c;下面给出了一个例子&#xff1a; ABCDEFG BABCDEF CBABCDE DCBABCD EDCBABC 这是一个5行7列的图形&#xff0c;请找出这个图形的规律&…

c语言 三角形三边abc,C语言代码输入abc三个数,求一这3个数为边长的三角形面积...

2011-01-04 回答#include #include #include #include #include int main(){float a 0.0;float b 0.0;float c 0.0;float s 0.0;double area 0.0;while(true){printf("input your date(a,b,c):");scanf("%f%f%f",&a,&b,&c);if(!isdigit((…

shell脚本中向hive动态分区插入数据

在hive上建表与普通分区表创建方法一样&#xff1b; 1 CREATE TABLE dwa_m_user_association_circle(2 device_number string, 3 oppo_number string, 4 prov_id_oppo string, 5 area_id_oppo string, 6 dealer_oppo string, 7 short_call_nums bigint, 8 long3…

WPF效果第二百零二篇之TreeView带连接线

前面文章中分享了TreeView支持多选;然而在项目上使用时,领导觉得不满意:体现不了真正的从属关系;既然领导都发话了;那就开整就行了;今天就再来个带有连接线的TreeView效果:1、来看看TreeViewItem的Template:2、展开和收缩动画:3、参考资料https://www.codeproject.com/tips/673…