使用 XmlPullParser (一)

上篇我们介绍了 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++
        }
    }
}

<<:  Day9 - pandas(4)Series与DataFrame的运算

>>:  Day 11 Chatbot integration- 看图学英文

Day 30. Hugo 系列文回顾,铁人赛反省与获得

前言 今天是铁人赛文章发文的最後一天 (在此我要谢谢我的爸爸、妈妈、老婆、小狗..),本篇会做个简单...

JavaScript入门 Day22_if判断2

今天要讲的是if...else,昨天只讲了if 那if...else 就是 如果...否则 来看看c...

DAY25 深度学习-卷积神经网路-Yolo v3

今天介绍一下Yolo v3, 首先在v3中使用了darknet-53的架构,架构如下图: 相比v2的...

[SwiftUI] 如何统计数字重复的次数

如果我有一个数字的阵列变数[2, 1, 2, 3, 5, 6, 8, 9],想要计算(0~9)各个数...