使用 Kotlin 读取大型 (+- 50Mb) Json 文件

Read large (+- 50Mb) Json file using Kotlin

提问人:Gabs 提问时间:5/2/2022 最后编辑:Gabs 更新时间:5/3/2022 访问量:796

问:

我开始使用“openweathermap.org”API 开发天气应用程序,它们以 Json 格式为您提供可用城市列表。

在我继续这个项目之前,我希望能够处理这个 Json 文件中的数据。

问题是每当我尝试读取和解析该文件时,我都会得到 Null。

代码如下:

主要活动:

class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        val jsonFileString = getJsonDataFromAsset(applicationContext, "citylist.json")
        Log.i("gabs Data", jsonFileString ?: "Empty Data")
        val gson = Gson()
        val listOfCities = object : TypeToken<List<CityList>>() {}.type
        var cities: List<CityList> = gson.fromJson(jsonFileString, listOfCities)
        cities.forEachIndexed { idx, city -> Log.i("data", "> Item $idx:\n$city") }
    }
}

Utils.kt:

fun getJsonDataFromAsset(context: Context, fileName: String): String? {
    val jsonString: String
    try {
        jsonString = context.assets.open(fileName).bufferedReader().use { it.readText() }
    } catch (ioException: IOException) {
        ioException.printStackTrace()
        return null
    }
    return jsonString
}

和数据类(城市数据数组):

class CityList : ArrayList<CityList.CityListItem>(){
    data class CityListItem(
        @SerializedName("coord")
        val coord: Coord,
        @SerializedName("country")
        val country: String,
        @SerializedName("id")
        val id: Double,
        @SerializedName("name")
        val name: String,
        @SerializedName("state")
        val state: String
    ) {
        data class Coord(
            @SerializedName("lat")
            val lat: Double,
            @SerializedName("lon")
            val lon: Double
        )
    }
}

和错误:

java.lang.RuntimeException:无法启动活动 ComponentInfo{com.example.weatherdisplay/com.example.weatherdisplay.ui.activities.MainActivity}:java.lang.NullPointerException:gson.fromJson(jsonFileString, listOfCities) 不得为空。

原因:java.lang.NullPointerException:gson.fromJson(jsonFileString, listOfCities) 不得为空 在 com.example.weatherdisplay.ui.activities.MainActivity.onCreate(MainActivity.kt:21)

json android-studio kotlin nullpointerexception

评论

0赞 cactustictacs 5/3/2022
读取该文件并将其解析为 JSON 是此处的两个独立步骤。是否包含数据,数据是否完整和正确?(您一读完它就会记录下来。如果没有,则问题出在读取文件时。如果是这样,则调用无法解析数据。如果您查看日志,它可能会记录警告,如果它像这样失败jsonFileStringgson.fromJson

答:

1赞 Daniel 5/3/2022 #1

您的代码中存在一些问题:

  1. 您没有关闭BufferedReader
  2. 您不应该在主线程上加载文件,因为它会阻止 UI

我创建了一些与您的数据结构相对应的示例数据:

[
  {
    "id": 1,
    "country": "Germany",
    "state": "Saxony",
    "name": "Dresden",
    "coord": {
      "lat": 0.0,
      "lon": 0.0
    }
  },
  {
    "id": 2,
    "country": "Germany",
    "state": "Berlin",
    "name": "Berlin",
    "coord": {
      "lat": 0.0,
      "lon": 0.0
    }
  },
  {
    "id": 3,
    "country": "Germany",
    "state": "Baden-Wuerttemberg",
    "name": "Stuttgart",
    "coord": {
      "lat": 0.0,
      "lon": 0.0
    }
  },
  {
    "id": 4,
    "country": "Germany",
    "state": "Hessen",
    "name": "Frankfurth",
    "coord": {
      "lat": 0.0,
      "lon": 0.0
    }
  },
  {
    "id": 5,
    "country": "Germany",
    "state": "Nordrhine-Westphalia",
    "name": "Cologne",
    "coord": {
      "lat": 0.0,
      "lon": 0.0
    }
  }
]

您的活动:

class MainActivity : AppCompatActivity() {

    companion object {
        const val TAG = "MyApplication"
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        lifecycleScope.launchWhenStarted {
            launch(Dispatchers.IO) {
                var reader: BufferedReader? = null
                try {
                    // Create a reader and read the file contents
                    reader = assets.open("data.json").bufferedReader()
                    val rawData = reader.use { it.readText() }

                    // Create a Type token that Gson knows how to parse the raw data
                    val cityListType = object : TypeToken<List<City>>() {}.type

                    // Parse the raw data using Gson
                    val data: List<City> = Gson().fromJson(rawData, cityListType)

                    // TODO: Do something with the data
                } catch (e: IOException) {
                    // Handle IOException: Gets thrown when the file wasn't found or something similar
                    Log.e(TAG, "An error occurred while reading in the data:", e)
                } catch (e: JsonParseException) {
                    // Handle JsonParseException: Gets thrown when there is a problem with the contents of the file
                    Log.e(TAG, "An error occurred while reading in the data:", e)
                }
                finally {
                    // Close the reader to release system resources
                    reader?.close()
                }
            }
        }
    }

}

您的数据结构:

data class City(
    @SerializedName("id")
    val id: Int,

    @SerializedName("country")
    val country: String,

    @SerializedName("state")
    val state: String,

    @SerializedName("name")
    val name: String,

    @SerializedName("coord")
    val coordinate: Coordinate
) {
    override fun toString(): String {
        return "#$id[$name $state $country]@[${coordinate.lat}|${coordinate.lon}]"
    }
}

data class Coordinate(
    @SerializedName("lat")
    val lat: Double,

    @SerializedName("lon")
    val lon: Double
)

在最好的情况下,您可以将获取文件内容并解析数据的代码放在 中,但这超出了此答案的范围。ViewModel

其他信息: https://developer.android.com/topic/libraries/architecture/viewmodelViewModels

评论

0赞 Daniel 5/3/2022
如果这个答案对你来说是正确的,请把它标记为接受的答案。