Home About Contact
Kotlin , Kotlin Native , XML

Kotlin Native で XML をパースする (XmlUtil を使用)

JavaVM で XML を扱う場合に XmlPullParser を使う例を このエントリーXmlPullParser を使ってXMLをパースする で書きました。 今度はこれと同じような処理を Kotlin Native でやってみたので備忘録としてそれを書き残します。 XmlPullParser の代りに XmlUtilを使います。

そういえば、XmlUtil を使った例は以前のエントリー Kotlin Native (linuxX64) で XML を扱うで書きました。ここではシリアライゼーションしないで、 直接 XML をパースします。

Gradle で Kotlin Native するには: https://kotlinlang.org/docs/native-gradle.html

環境

$ java -version
openjdk version "17.0.10" 2024-01-16
OpenJDK Runtime Environment (build 17.0.10+7-Ubuntu-122.04.1)
OpenJDK 64-Bit Server VM (build 17.0.10+7-Ubuntu-122.04.1, mixed mode, sharing)

$ gradle -version
Gradle 8.4

OS は Ubuntu 22.04 LTS です。

hello-xml プロジェクト

$ mkdir hello-xml
$ cd hello-xml
$ touch build.gradle.kts
$ touch settings.gradle
$ touch gradle.properties

build.gradle.kts の内容:

plugins {
    kotlin("multiplatform") version "1.9.23"
}

version = "0.1"

repositories {
    mavenCentral()
}

kotlin {
    // macosX64("native") { // on macOS
    // mingwX64("native") { // on Windows
    linuxX64("native") { // on Linux
        binaries {
            executable()
        }
    }

    sourceSets {
         nativeMain {
            dependencies {
                implementation("io.github.pdvrieze.xmlutil:core:0.86.3")
            }
        }
    }
}

tasks.withType<Wrapper> {
    gradleVersion = "8.4"
    distributionType = Wrapper.DistributionType.BIN
}

settings.gradle ファイルを用意して次の内容を記述します。

rootProject.name = "hello"

今回は hello.kexe というファイル名でバイナリを生成したいので rootProject.namehello を設定しました。

gradle.properties ファイルを用意して次の内容を記述します。

kotlin.mpp.applyDefaultHierarchyTemplate=false

続いて App.kt の用意:

$ mkdir -p src/nativeMain/kotlin/
$ touch src/nativeMain/kotlin/App.kt

src/nativeMain/kotlin/App.kt:

import nl.adaptivity.xmlutil.XmlReader
import nl.adaptivity.xmlutil.xmlStreaming
import nl.adaptivity.xmlutil.EventType

fun main() {
    val text = "<html><body><p>Hello, World!</p></body></html>"

    val xr: XmlReader = xmlStreaming.newReader(text)

    var eventType: EventType = xr.next()
    while( eventType != EventType.END_DOCUMENT ){

        when(eventType){
            EventType.START_DOCUMENT -> println("Start Document")
            EventType.START_ELEMENT -> println("Start Element: ${xr.name}")
            EventType.END_ELEMENT -> println("End Element: ${xr.name}")
            EventType.TEXT -> println("Text: ${xr.text}")
            else -> {
                println("Unknown EventType: ${eventType}")
            }
        }

        eventType = xr.next()
    }
}

おおむね前回のエントリーのコードと同じです。

ここで実行して作動を確かめます。

$ gradle runDebugExecutableNative

> Task :runDebugExecutableNative
Start Document
Start Element: html
Start Element: body
Start Element: p
Text: Hello, World!
End Element: p
End Element: body
End Element: html

必要であれば gradle wrapper しておきましょう。

$ gradle wrapper

あとは while している部分を再帰関数に書きかえてもよいのですが、 前回と同じ話を繰り返すだけなので省略します。

もし属性をパースする場合は次のように記述します(抜粋)。

EventType.START_ELEMENT -> {
    val attrs = 0.until(xr.attributeCount).map { index->
        val name  = xr.getAttributeName(index)
        val value = xr.getAttributeValue(index)
        "$name=$value"
    }.joinToString(",")

    println("${xr.name}, ${attrs})")
}

Linux(X64) 用のNativeバイナリ実行ファイルを生成するには nativeBinaries タスクを使用。

$ ./gradlew nativeBinaries

次の場所にそれが生成されます: ./build/bin/native/releaseExecutable/hello.kexe

以上です。