iT邦幫忙

2021 iThome 鐵人賽

DAY 6
0

上篇我們介紹了 XmlPullParser 和常用的幾個 event type ,現在我們來介紹它的實作。首先我們要拿到 parser 的實體才能夠操作 tag ,我們可以定義一個 function 來拿 parser ,XML 字串拿到之後要先轉成 ByteArrayInputStream ,然後安全使用 use 來讀 inputStream 。為什麼會講它安全呢?因為 Kotlin 原生的 use extension 可以讓我們操作 inputStream 的時候,不用自己寫 try/catch/finally ,它裡面幫你包好好的,只要傳一個 code block 進去執行就好。

fun getXmlParser(xml: String): XmlPullParser {
    ByteArrayInputStream(xml.toByteArray()).use { inputStream ->
        return Xml.newPullParser().apply {
            setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, false)
            setInput(inputStream, null)
            nextTag()
        }
    }
}

拿到parser 實體後,讓我們先從怎麼 parse 最外層的 <channel> 開始吧!

fun <T> parseChannel(xml: String, action: XmlPullParser.() -> T): T {
    val parser = getXmlParser(xml)

    var result: T? = null
    while (parser.next() != XmlPullParser.END_TAG) {
        if (parser.eventType != XmlPullParser.START_TAG) continue

        if (parser.name == CHANNEL) {
            result = action(parser)
            break
        } else {
            parser.skip()
        }
    }
    return result ?: throw XmlPullParserException("No valid channel tag in the RSS feed.")
}

這段程式碼可以看到我們參數裡面放了個 action: XmlPullParser.() -> T ,這個是一個 Kotlin receiver type 的 lambda ,主要是想要讓外面使用的人在 lambda 裡面可以使用 XmlPullPaser 的 API ,另外,還有個泛型 T ,會使用泛型的主要原因是便於這個 function 被重複使用,畢竟我們有可能會定義不同型態的資料格式,舉例來說像是我可以把 RSS 2.0 的標準資料定義成 RssStandardChannel ,iTunes 平台格式定義成 ITunesChannel ,google 的就放在 GoogleChannel ,這些都可以放進這個 function 來用。

使用 parser 的時候,我們可以透過迴圈去拿取下一個 event ,拿 eventType 和 name 來判斷是不是 Channel ,這邊因為我們只需要掃到 channel 其他的 event 就可以跳過。拿到 channel 的 event 後,我們就可以用已經定義好的 lambda 去爬下一層的 tag 。

這邊還有個小細節就是跳過 tag 的方式,這邊一樣是寫成一個 extension 方便之後重複使用。跳過的 tag 有可能會有很多子 tag , depth 就是在記錄層數,當它回到 0 的時候,代表這個 tag 底下整顆子樹都跳過了。

@Throws(XmlPullParserException::class, IOException::class)
protected fun XmlPullParser.skip() {
    if (eventType!= XmlPullParser.START_TAG) {
        throw IllegalStateException()
    }
    var depth = 1
    while (depth != 0) {
        when (next()) {
            XmlPullParser.END_TAG-> depth--
            XmlPullParser.START_TAG-> depth++
        }
    }
}

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

尚未有邦友留言

立即登入留言