iT邦幫忙

2021 iThome 鐵人賽

DAY 3
0
Mobile Development

如何使用 Kotlin Annotation Processor 做出自己的 Custom Data Parser Library系列 第 3

使用 DOM Parser 取值

這篇會講解怎麼樣用 DOM 的 parser 把 RSS 資訊拿出來,首先我們可以先 new 一個 DocumentBuilder

val builder = DocumentBuilderFactory.newInstance().newDocumentBuilder()

接著把 XML 字串塞進去 builder 裡,塞之前我們要先轉成 ByteArrayInputStream ,就可以拿到一個 document 。

val document = builder.parse(xml.byteInputStream())

我們可以從 document 裡面拿到指定 tag 的節點列表,下方就是拿 <channel> 底下的子節點列表。

const val CHANNEL = "channel"
val nodeList = document.getElementsByTagName(CHANNEL)

拿到節點列表之後我們就可以對節點做操作了!但這邊因為 RSS 格式中預設只有一個 <channel> 所以我們可以寫一個 parserChannel 的 function 。從 nodeList 拿出來的 node 要轉型成 Element 才可以進行操作。這個 function 的第二個參數 action 是一個 lambda ,讓使用者決定拿到 channel 的 element 後要進行的動作。

inline fun <T> parseChannel(xml: String, action: Element.() -> T): T {
      val builder = DocumentBuilderFactory.newInstance().newDocumentBuilder()
      val document = builder.parse(xml.byteInputStream())
      document.documentElement.normalize()
      val nodeList = document.getElementsByTagName(CHANNEL)
      var result: T? = null

      if (nodeList?.length == 1) {
          val element = nodeList.item(0) as? Element
          element?.let {
              result = action(it)
          }
      }
      return result ?: throw IllegalArgumentException("No valid channel tag in the RSS feed.")
  }

前一篇的文章有提到 DOM parser 有個特色就是可以跨層查詢,但我們怎麼指定我們要查詢哪一個 tag 底下的值?其實可以透過指定 parent tag 的方式去查找。這是為了解決一個可能遇到的問題:假如我們今天要找 <channel> tag 底下的 <title> ,這個 title 可能存在於 <channel> 底下,但也可能存在於 <image> 底下。

<channel>
      <title>channel title</title>
			<image>
         <link>http://channel.image.link</link>
         <title>channel image title</title>
			</image>
</channel>

為了解決上面情境發生的問題,我可以寫一個 Element 的 extension function ,讓我們可以取某個 tag 的值出來,但可以指定 parent 的 tag name 。

fun Element.readString(name: String, parentTag: String? = null): String? {
      val nodeList = getElementsByTagName(name)
      if (parentTag == null) {
          return nodeList.item(0)?.textContent
      } else {
          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) continue

              return e.textContent
          }
          return null
      }
  }

上一篇
XML Parsers
下一篇
使用 DOM Parser 取屬性
系列文
如何使用 Kotlin Annotation Processor 做出自己的 Custom Data Parser Library30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言