WebAPIで取得したJSON形式データをデシリアライズ-Gson.fromJson() List<T>-

WEBに転がっているJSONデータをKotlin内で配列やリストに入れたい
(okhttp3で取得したJSONデータをKotlinのdata classでList化するよメモ)

## 準備
レイアウトの記述を簡易にするためViewBindingを使用

https://developer.android.com/topic/libraries/view-binding?hl=ja

デバイス（またはシミュレータ）のインターネット環境が整っていない、permissionが設定されていないと以下のエラーが出るか後述のonFailure()に入ってしまう。
```kotlin:Logcat
java.io.IOException: canceled due to java.lang.SecurityException: Permission denied (missing INTERNET permission?)
```
#### HTTP通信許可
```xml:build.gradle

<uses-permission android:name="android.permission.INTERNET" />
<!-- 今回は必要ないがhttpsのsがない通信をする場合に許可がいる -->
    <application
        android:usesCleartextTraffic="true"
        tools:targetApi="31">
```

## パッケージimport
※最新 Ladybug Feature Drop | 2024.2.2版
```kotlin:build.gradle(Project)
plugins {
    alias(libs.plugins.android.application) apply false
    alias(libs.plugins.kotlin.android) apply false
    alias(libs.plugins.kotlin.serialization)apply false
}
```
```kotlin:build.gradle(Module)
plugins {
//省略
    alias(libs.plugins.kotlin.serialization)//追記
}
//省略
dependencies {
    implementation(libs.http3)
    implementation(libs.json)
    implementation(libs.gson)
}
```
```toml:toml
[versions]
com = "1.6.20"
http = "4.9.1"
ser = "1.3.2"
convert = "2.3.0"
[libraries]
http3 = {group = "com.squareup.okhttp3",name = "okhttp",version.ref ="http"}
json = {group = "org.jetbrains.kotlinx",name = "kotlinx-serialization-json",version.ref = "ser"}
gson = {group = "com.squareup.retrofit2",name = "converter-gson",version.ref = "convert"}
[plugins]
kotlin-serialization = { id = "org.jetbrains.kotlin.plugin.serialization", version.ref = "com" }

```
#### JSONデータ
気象庁が提供しているデータを使わせていただきます。
熊谷地方気象台
https://www.jma.go.jp/bosai/forecast/data/overview_forecast/110000.json
```ruby:JSON
{"publishingOffice":"熊谷地方気象台",
"reportDatetime":"2024-00-00T00:00:00+00:00",#←リアルタイムな日付
"targetArea":"埼玉県",
"headlineText":"",
"text":"　日本付近は冬型の気圧配置となっています。\n\n　
    埼玉県は、曇りや晴れで、雨の降っている所があります。\n\n　
    １８日は、引き続き冬型の気圧配置となる見込みです。
    このため、南部は曇り夕方から晴れで昼前雨の降る所があるでしょう。北部と秩父地方は晴れる見込みです。\n\n　
    １９日は、冬型の気圧配置は次第に緩み、高気圧が日本海に移動するでしょう。
    このため、晴れ夜曇りとなる見込みです。"}
```
#### DataClassの準備
JSONのカラムをそのままに設定
```kotlin:Weather.kt
data class Weather(
    val publishingOffice:String,
    val reportDatetime:Date,
    val targetArea:String,
    val headlineText:String,
    val text:String
)
```
## OkHttpリクエスト
まずはGson.fromJson() List\<T>は一旦忘れて
リクエストがきちんと通るか見てみよう。

###### ※メインスレッドでは通信ができない。
OkHttpリクエスト時は強制的にバックグラウンドで非同期通信を行っていることは覚えておこう。
enqueueを利用しCallbackで同期っぽくなっているはずだ。

https://android.gcreate.jp/network_request_with_okhttp/

### Get通信
方法はGETかPOSTとなる。
初心者はGETが無難。
焦らずOkHttpGetクラスを作成しよう。
成功でも失敗でもCallbackが終えた直後にHandlerがメインスレッドに返してくれるので安心だ。

```kotlin:OkHttpGet.kt
import android.os.Handler
import android.os.Looper
import android.util.Log
import okhttp3.Call
import okhttp3.Callback
import okhttp3.OkHttpClient
import okhttp3.Request
import okhttp3.Response
import java.io.IOException
import java.util.concurrent.TimeUnit

class OkHttpGet {
    //メインスレッドへ結果を返す有り難い装置
    private val mainHandler: Handler
        get() = Handler(Looper.getMainLooper())

    private val connectionTimeOut = "10000"
    private val readTimeOut = "10000"
    private val client = OkHttpClient.Builder()
        .connectTimeout(connectionTimeOut.toLong(), TimeUnit.MILLISECONDS)
        .readTimeout(readTimeOut.toLong(), TimeUnit.MILLISECONDS)
        .build()
    fun getRequest(endProcess:(String) -> Unit){
        val request = Request.Builder()
            .url("https://www.jma.go.jp/bosai/forecast/data/overview_forecast/110000.json")
            .build()
        client.newCall(request).enqueue(object : Callback {
            override fun onResponse(call: Call, response: Response) {
                val responseBody = response.body?.string().orEmpty()
                Log.e("OK", responseBody)
                mainHandler.post {
                    endProcess(responseBody)
                }
            }
            override fun onFailure(call: Call, e: IOException) {
                Log.e("Error", e.toString())
                mainHandler.post{
                    endProcess(e.toString())
                }
            }
        })
    }
}

```
呼び出すメインスレッド側のMainActivity
```kotlin:MainActivity.kt
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
// Viewの簡易記述
import com.example.アプリ名.databinding.ActivityMainBinding
import okhttp3.OkHttpClient

private lateinit var binding: ActivityMainBinding

class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = ActivityMainBinding.inflate(layoutInflater)
        setContentView(binding.root)

        Log.e("route⓵", "GET START")
        OkHttpGet().getRequest{
            Log.e("route⓸", "GET RESULT")
            binding.textV.text = it
        }
        Log.e("route⓶", " GET END")
    }
    override fun onStart() {
        super.onStart()
        Log.e("route⓷", "onStart")
    }
}
```
一応レイアウト
```xml:activity_main.xml
<!--ここのIDがbinding.～の記述になる-->
 <TextView
        android:id="@+id/text_v"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Hello World!"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />
```
これでTextViewへ結果が反映されているはずだ。
Logの確認をするとonCreateを抜けた後にOkHttpGet().getRequestの中へ
メインスレッドとして戻ってきている確認が取れる。

```:Logcat
    route⓵   E  GET START
    route⓶   E  GET END
    route⓷   E  onStart
    route⓸   E  GET RESULT
```

## 仕上げ-Gson.fromJson() List\<T>-
では以下を追記しList\<dataclass>として仕上げよう。
```kotlin:MainActivity.kt
import com.google.gson.reflect.TypeToken
import com.google.gson.Gson
//# ・・・中略
    OkHttpGet().getRequest{
                val listType = object:TypeToken<List<Weather>>() {}.type
                val jsonData = Gson().fromJson<List<Weather>>("[${it}]", listType)
                Log.e("結果", jsonData[0].publishingOffice)
                Log.e("結果", jsonData[0].targetArea)
                Log.e("結果", jsonData[0].reportDatetime.toString())
                Log.e("結果", jsonData[0].text)
            }
```
型（ここではreportDatetimeのDate型）には注意が必要。
実際のJSONは"reportDatetime":"2024-11-21T04:39:00+09:00"という文字列だ。
```Logcat
    結果 E 熊谷地方気象台
    結果 E 埼玉県
    結果 E Thu Nov 21 04:39:00 GMT+09:00 2024
    結果 E　伊豆諸島の東には低気圧があって東北東へ進んでいます。#...略
```
今回は取得したJSONを[]で囲み強制的に形式を合わせている。

https://qiita.com/iKimishima/items/10e1d45d6cc660b037a2
## おまけ
#### 旧バージョン
```kotlin:build.gradle(Project)
plugins {
    id("com.android.application") version "8.1.3" apply false
    id("org.jetbrains.kotlin.android") version "1.9.0" apply false
    id("org.jetbrains.kotlin.plugin.serialization") version "1.6.20" apply false
}
```
```kotlin:build.gradle(Module)
plugins {
    id("com.android.application")
    id("org.jetbrains.kotlin.android")
    id("org.jetbrains.kotlin.plugin.serialization")
}
android {
// ・・・
//#中略
        buildFeatures {
            viewBinding =true
            dataBinding =true
        }
}
dependencies {
    // ・・・
    //#中略
    //http3
    implementation("com.squareup.okhttp3:okhttp:4.9.1")
    //JSON
    implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.3.2")
    implementation("com.squareup.retrofit2:converter-gson:2.3.0")
}
```