Help us understand the problem. What is going on with this article?

JSON 以外のデータ形式を Retrofit で扱う(なら Converter を実装する)

概要

JSON 以外のデータ形式を Retrofit で扱う方法について述べます。時間がない方は Retrofit の公式 を見てください。

前提

Retrofit2 の使い方については知っているものとします。

なぜやるのか?

趣味で RSS リーダーを自作したいと思った際に、XML を Retrofit で扱うにはどうしたら良いのかとわからなくなり、だったら自分で実装しようと思ってやってみたので、それを残しておきます。

RSS は XML の派生形式です。JSON ではありません。かつては、Retrofit で XML を扱う際は Simple XML を使うのが一般的のようでした。

Deprecation

……が、いま SimpleXmlConverterFactory をコードに書くと Deprecated の警告が出ます。

リポジトリーを見てみると、

@deprecated we recommend switching to the JAXB converter.

「JAXB の Converter を使いましょう」とのことです。

does not work on Android.

ではと思って JAXB Converter を見に行くと、README に以下の記述があります。

Note that JAXB does not work on Android.

ちょっと何て書いてあるか英語がよくわからないですね。 Android で XML を処理したいという開発者のニーズは存在しなくなったのでしょうか?

Issue

どうしたらよいのかと思って調べたら、別に悩んでいる人がいました
……が、特に解決策があるわけでもないようです。

Solution?

もうちょっと探したら、以下のQ&Aを発見しました。 Tickaroo TikXML というものがあるらしいです。
What kind of XmlConverter can I use for Retrofit in Android?

GitHub Repository

Tickaroo/tikxml

Retrofit2 用の Converter も用意されています。

implementation 'com.tickaroo.tikxml:retrofit-converter:0.8.15'

普通の方はこれを用いると良いでしょう。

My solution?

上の Converter の導入が上手くいかないので諦め、ほかにどうしたらよいのかと思って調べたら、
faruktoptas/RetrofitRssConverterFactory という Converter を見つけました。 これを見ると、自分で Converter を作ればどんなレスポンスでもオブジェクトに Mapping できるようです。

RSS のパースに関しては過去に自分で作った Parser があるので、せっかくだからそれを使ってみました。

Official solution

これは大発見でも何でもなく、 Retrofit の公式 にちゃんと書いてありました。

If you need to communicate with an API that uses a content-format that Retrofit does not support out of the box (e.g. YAML, txt, custom format) or you wish to use a different library to implement an existing format, you can easily create your own converter. Create a class that extends the Converter.Factory class and pass in an instance when building your adapter.

というわけで、HTTP 通信で JSON 以外のデータをやり取りするアプリでは、 Retrofit の Converter をがんばって作りましょう。もしくは JSON に置き換えましょう。

実装

Converter の実装

今回重要なのはここです。retrofit2.Converter インターフェイスを実装します。
convert メソッドで、受け取った OkHttp3 の ResponseBody を特定の型に変換します。
この処理を変えることで、あらゆるデータ形式を扱うことができるようになります。

class RssResponseConverter : Converter<ResponseBody, Rss> {

    private val parser = Parser()

    override fun convert(responseBody: ResponseBody): Rss? {
        val bodyString = responseBody.string()
        return parser.parse(bodyString.split(findSplitter(bodyString)))
    }

    private fun findSplitter(body: String) =
            if (body.contains(LINE_SEPARATOR)) LINE_SEPARATOR else "\n"

    companion object {
        private val LINE_SEPARATOR = System.getProperty("line.separator")
    }
}

Parser

今回は勉強だと思ってゴリゴリのパーサーを自作しました。実際の開発は既存のライブラリーを用いましょう。

Converter.Fatctory の実装

先ほど実装した Converter のインスタンスを生成するファクトリーを返すクラスです。

class RssConverterFactory private constructor(): Converter.Factory() {

    override fun responseBodyConverter(
            type: Type?,
            annotations: Array<Annotation>?,
            retrofit: Retrofit?
    ): Converter<ResponseBody, *> = RssResponseConverter()

    companion object {

        fun create() = RssConverterFactory()
    }
}

Converter の利用

あとは addConverterFactory で自作の ConverterFactory を指定して、Retrofit を普通に使いましょう。Retrofit の普通の使い方については多数の記事があるので、ここでは特に触れません。

class RssReaderApi {

    private val converter = RssConverterFactory.create()

    @WorkerThread
    operator fun invoke(rssUrl: String): Rss? {
        val uri = rssUrl.toUri()
        val retrofit = Retrofit.Builder()
                .baseUrl("${uri.scheme}://${uri.host}")
                .addConverterFactory(converter)
                .build()

        val service = retrofit.create(RssService::class.java)
        val call = service.call(rssUrl)
        return call.execute().body()
    }
}

一応書いておくと RssService インターフェイスはこんな感じです。

interface RssService {

    @GET
    fun call(@Url url: String): Call<Rss?>
}

おわりに

JSON 以外のデータ形式、今回は XML を Retrofit で扱う方法について述べました。

で、これを知って何の役に立つの?

自分で Converter を作れば、どんなデータ形式でも(CSVやYAML等々)扱うことができるということだけ知っておけば良いかと思います。
新しいデータ形式や社内独自のデータ形式も Retrofit で扱えて幸せになるかもしれません。

そして、メジャーなデータ形式であれば、誰かが Converter を作っているかもしれないので、それを GitHub から探し出した方が早いかもしれない、ということを知っておくのも大事なことです。

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした