kotlin 高阶函数 DSL 解析HTML
前言
使用纯代码 加 注释的方式,可以更快的理解源码
如果你喜欢,请点个赞,后期会不断的深入讲解
一、kotlin 高阶函数 解析 HTML
先看一张图
代码如下
import java.io.File
// 面向对象思维, 设计 类的关系,(依赖倒转原则)面向对象 而 不面向细节
private interface Element { // 元素顶级接口
/**
* buffer 拼接所有元素
* indent 缩进效果
*/
fun run(builder: StringBuilder, indent: String)
}
// 从最简单的 text 文本开始
private class TextElement(val text: String) : Element {
override fun run(builder: StringBuilder, indent: String) {
// 缩进后,显示文本内容
builder.append("$indent$text\n")
}
}
// 开始写复杂的部分 <html> <head>
private open class Tag(val tagName: String) : Element { //tagName 文本的 Element 如 <html> <head>
// 集合,每一个元素都是 Element
val elements = arrayListOf<Element>()
// Map 集合, 每一个元素 key = 元素名, Value = 属性对应的值
val attribute = hashMapOf<String, String>()
override fun run(builder: StringBuilder, indent: String) { // 被 toString触发
// 缩进<html 属性> <head>
builder.append("$indent<$tagName${renderAttributes()}>\n")
for (element in elements) {
element.run(builder, indent + " ")
}
// </html> 闭合
builder.append("$indent</$tagName>\n")
}
// 属性拼装 href = "https://blog.csdn.net/u010755471"
// null 没有属性
private fun renderAttributes(): String? {
val builder = StringBuilder()
for (key in attribute.keys) {
// 空格: <a href
builder.append(" $key=\"${attribute[key]}\"")
}
return builder.toString()
}
override fun toString(): String {
val stringBuilder = StringBuilder()
run(stringBuilder, "") // 参数1: stringBuilder 方便组装HTML 参数, 参数二:indent 不要缩进
return stringBuilder.toString()
}
}
private open class TagClass(tagName: String) : Tag(tagName) {
operator fun String.unaryPlus() { // 运算符重载 +
elements.add(TextElement(this)) // elements.add
}
operator fun String.unaryMinus() { // 运算符重载 -
elements += TextElement(this)
}
}
// 第一个中转站 Html
private class Html : TagClass("html") {
fun head(action: Head.() -> Unit) {
val newHead = Head()
newHead.action()
elements += newHead // 添加进数组
}
fun body(action: Body.() -> Unit) {
val newBody = Body("body")
newBody.action()
elements += newBody
}
}
// Head 中转站
private class Head : TagClass("head") {
fun title(action: Title.() -> Unit) {
val newTitle = Title()
newTitle.action()
elements.add(newTitle)
}
}
// Body 中转站
private open class Body(tagName: String) : TagClass(tagName = tagName) {
fun h1(action: H1.() -> Unit) {
val newH1 = H1()
newH1.action()
elements += newH1
}
fun p(action: P.() -> Unit) {
val newP = P()
newP.action()
elements += newP
}
open fun a(href: String, action: A.() -> Unit) {
val newA = A()
newA.href = href
newA.action()
elements += newA
}
}
// h1 中转站
private class H1 : Body("h1") {
}
// P 中转站
private class P : Body("p") {
fun b(action: B.() -> Unit) {
val newB = B()
newB.action()
elements += newB
}
override fun a(href: String, action: A.() -> Unit) {
val newA = A()
newA.href = href
newA.action()
elements += newA
}
fun ul(action: Ul.() -> Unit) {
val newUl = Ul()
newUl.action()
elements += newUl
}
}
// A 中转站
private class A : Body("a") {
var href: String
get() = attribute["href"]!!
set(value) {
attribute["href"] = value
}
}
// P 中转站
private class B : Body("b") {
}
// Ul 中转站
private class Ul : Body("ul") {
fun li(action: Li.() -> Unit) {
val newLi = Li()
newLi.action()
elements.add(newLi)
}
}
// Li 中转站
private class Li : Body("li") {}
// Title 中转站
private class Title : TagClass("title") {
}
private fun html(action: Html.() -> Unit): Html {
val html = Html()
html.action()
return html
}
fun main(args: Array<String>) {
val names = listOf("张三", "大漂亮", "王美丽")
val result =
html { // this == 第一个中转站 { head body 。。 }
head { // this == head中转站 { title }
title { +"使用 Kotlin 进行 HTML 编码" }
}
body { // this == body中转站 { h1 p a p }
h1 { // this == h1中转站 { 未知 }
}
p { -"此格式可用作 HTML 的替代标记" }
// 具有属性和文本内容的元素
a(href = "https://blog.csdn.net/u010755471") { -"不爱学习的猪的博客" }
// 混合内容
p {
-"Derry老师来了"
b { -"Derry是谁" }
-"文本。有关更多信息,请参阅"
a(href = "https://blog.csdn.net/u010755471") { -"不爱学习的猪的博客" }
-"Derry的项目"
}
p { -"一些文字" }
// 从命令行参数生成的内容
p {
-"命令行参数是:"
ul { // this == UL中转站 { li 子标签 }
for (name in names)
li { -name } // this == LI中转站
}
}
}
}
println(result)
val file = File("/Users/Documents/Android leaning work/kotlinleaning04/testHtml.html")
file.writeText(result.toString())
}
二、使用 DSL 解析HTML
先来张图
源码如下:
import java.io.File
// 定义一个节点接口
interface Node {
fun create(): String
}
// 中转站
class BlockNode(val name: String) : Node {
val children = ArrayList<Node>() // 节点集合: html head body
private val properties = hashMapOf<String, Any>() //属性集合:style='color: white; font-family: Microsoft YaHei'
override fun create(): String {
return """<$name ${properties.map { "${it.key}='${it.value}'" }.joinToString(" ")}>${children.joinToString ( "" ){it.create()}}</$name"""
}
operator fun String.invoke(action: BlockNode.() -> Unit){
val stringNode = BlockNode(this)
stringNode.action()
this@BlockNode.children += stringNode
}
operator fun String.invoke(value: Any){
this@BlockNode.properties[this] = value
}
operator fun String.unaryPlus(){
val stringNode = StringNode("$this &sbsp; &sbsp;")
this@BlockNode.children += stringNode
}
}
class StringNode(private val value: String) : Node {
override fun create(): String {
return value
}
}
fun html(action: BlockNode.() -> Unit): BlockNode {
val blockNode = BlockNode("html")
blockNode.action()
return blockNode
}
fun BlockNode.head(action: BlockNode.() -> Unit) {
val head = BlockNode("head")
head.action()
children += head
}
fun BlockNode.body(action: BlockNode.() -> Unit) {
val body = BlockNode("body")
body.action()
children += body
}
fun main() {
val htmlContent = html { // this持有中转站BlockNode
head { // this持有中转站BlockNode
// String.invoke(Any)
"meta" { "charset"("UTF-8") }
}
body {
"div" {
"style"(
"""
width: 666px;
height: 666px;
line-height: 600px;
background-color: #F00;
text-align: center
""".trimIndent()
)
"span" {
"style"(
"""
color: white;
font-family: Microsoft YaHei
""".trimIndent()
)
+"你好 HTML DSL!!"
+"我就是我,不一样的烟火"
+"像我这样牛逼的人"
+"世界上还有几人"
}
}
}
}.create() // 用户调用create函数,我就组装
println(htmlContent)
File("/Users/tiger/Documents/Android leaning work/kotlinleaning04/ttt.html")
.writeText(htmlContent)
}
总结
🤩
🎉 原 创 不 易 , 还 希 望 各 位 大 佬 支 持 一 下 \textcolor{blue}{原创不易,还希望各位大佬支持一下} 原创不易,还希望各位大佬支持一下
👍 点 赞 , 你 的 认 可 是 我 创 作 的 动 力 ! \textcolor{green}{点赞,你的认可是我创作的动力!} 点赞,你的认可是我创作的动力!
🌟 收 藏 , 你 的 青 睐 是 我 努 力 的 方 向 ! \textcolor{green}{收藏,你的青睐是我努力的方向!} 收藏,你的青睐是我努力的方向!
✏️ 评 论 , 你 的 意 见 是 我 进 步 的 财 富 ! \textcolor{green}{评论,你的意见是我进步的财富!} 评论,你的意见是我进步的财富!