使用 DOM Parser 取属性

讲完了怎麽取 tag 的值,我们来取 tag 夹带的属性。假设我们拿到了下面的 RSS 内容:

<channel>	
      <category>channel category 1</category>
      <category domain="http://channel.category.domain">channel category 2</category>
</channel>

首先我们先取 <category> 的 attribute ,但我们在处理 parser 的时候有个基本原则就是不能够预设资料一定存在,所以 null safty 处理在这边是需要非常小心!对 Element 取值只要用原生的 function getAttribute 就好了。

fun Element.getAttributeOrNull(tag: String): String? {
    val attr = getAttribute(tag) ?: return null
    return if (attr.isEmpty() || attr.isBlank()) null else attr
}

接着我们可以来处理怎麽拿到 <category> 的资讯,上面的情况我们拿到读 category 不只一个,而且内容物不一定相同。我可以自己定义一个 data class 去包这些资讯,但他内含的资讯有可能是 nullable 的。

data class Category(
    val name: String?,
    val domain: String?,
)

之後我们就可以把上面的东西整合一下,写出一个专门读 <category> 列表的 extension function 。

fun Element.readCategories(parentTag: String, tagName: String): List<Category>? {
    val result = mutableListOf<Category>()
    val nodeList = getElementsByTagName(tagName) ?: return null

    for (i in 0 until nodeList.length) {
        val e = nodeList.item(i) as? Element ?: continue
        val parent = e.parentNode as? DeferredElementImpl
        if (parent?.tagName == parentTag || parent?.tagName == tagName) {
            result.add(Category(name = e.getAttributeOrNull(TEXT), domain = null))
        }
    }
    return result
}

那上面为什麽又需要 parentTag 呢?因为 category 这个 tag 有可能存在在其他的子节点里,比方说像下面这样:

<channel>
		<category>channel category 1</category>
        <category domain="http://channel.category.domain">channel category 2</category>
		<item>
            <category>item category 1</category>
		</item>
		<item>
            <category>item category 2</category>
		</item>
</channel>

总结一下目前我们写的 Custom DOM Parser 做的事情:

  1. 取某个 tag 的 value 。
  2. 取某个 tag 的 attribute 。
  3. 跨层搜寻某个 tag 。
  4. 抓取 tag 底下的 value 和 attribute ,像是上面的 category 列表。

现在这种实作方式,每遇到一种样子的 tag ,我们就有可能需要多写一个 function 和多定义一个 data class 去包里面的资讯。Kotlin 的实作方式可以让我们在有支援 Kotlin 的平台上,去操作这些 parser ,但我们也可以透过平台专属的 API 去优化我们的 parser ,下篇文章我们就会开始以 Android 为例去优化我们的 Kotlin parser 。


<<:  Day 09 Azure Storage Account- 给照片找个家

>>:  从登入画面开始做起(下)Day4

Day14:堆积排序(Heap Sort)

堆积(Heap) 堆积,是一种树状结构,用於实现「优先伫列(Priority queue)」。Pri...

资安学习路上- Injection的爱恨情仇5

语法拼接 前面Injection的爱恨情仇4讲到SQL injection常发生在语法拼接的地方,这...

Day03 - 一边动手修改 Vue CLI 建立的专案一边复习 Vue 指令与资料夹结构

今天重开一了个新的 Vue CLI 专案 因为这次想要练习将 component 引入主 App.v...

D22 - 如何用 Apps Script 自动化地创造与客制 Google Slides?(三)一次看完所有档案的预览

今天的目标: 当要整理 Google Drive 时,会发现好多的档案、文件不确定哪个要怎麽做。一个...

如何使用 UML 序列图对 MVC 框架进行建模?

MVC(或模型-视图-控制器)是一种流行的软件框架,用於成功有效地将用户界面与底层数据模型相关联。由...