2

我需要解析从服务器下载的非常大的 JSON 文件。这些 JSON 文件可以包含完全不同的键和值。这里有些例子...

{ "result": "PASS", 
  "items": [ 
    { "name": "John", "age": 33 }, 
    { "name": "Jane", "age": 23 } ] 
}


{ "result": "PASS", 
  "items": [ 
    { "make": "Ford", "model": "Mustang", "colors": ["blue", "red", "silver"] }, 
    { "make": "Dodge", "model": "Charger", "colors": ["yellow", "black", "silver"] } ] 
}

items数组可能包含数千个条目,每个项目中的数据最多可以包含 60 个键/值对。

这些只是两个示例,但我需要能够解析 30-40 种不同类型的 JSON 文件,而且我不能总是控制文件中的数据类型。因此,我无法创建自定义模型来将数据绑定到我的应用程序中的对象。

我想要做的是为数组JsonObject中的每个项目创建一个并将其添加到我可以在应用程序中使用的一个。我目前正在使用 Klaxon Streaming API 来尝试完成此操作,但似乎可以找到一种无需绑定到自定义对象的方法。itemsMutableList

JsonReader(StringReader(testJson)).use { reader ->
        reader.beginObject {
            var result: String? = null
            while (reader.hasNext()) {
                val name = reader.nextName()
                when (name) {
                    "result" -> result = reader.nextString()
                    "items" -> {
                        reader.beginArray {
                            while (reader.hasNext()) {
                                // ???
                            }
                        }
                    }
                }
            }
        }
    }
4

1 回答 1

1

如果您无论如何都要将所有项目收集到一个列表中(而不是立即一个接一个地处理它们),那么使用流 API 没有多大意义。它可以做得更简单:

val response = Klaxon().parseJsonObject(StringReader(testJson))
val result = response["result"]
val items = response.array<JsonObject>("items") ?: JsonArray()
...

流处理涉及更多。首先,您要确保在开始处理之前服务器响应未完全读入内存(即解析器输入不应是字符串,而是输入流。详细信息取决于您的 http 客户端库选择)。其次,您需要提供某种回调,以便在物品到达时对其进行处理,例如:

fun parse(input: Reader, onResult: (String) -> Unit, onItem: (JsonObject) -> Unit)  {

    JsonReader(input).use { reader ->
        reader.beginObject {
            while (reader.hasNext()) {
                when (reader.nextName()) {
                    "result" -> onResult(reader.nextString())
                    "items" -> reader.beginArray {
                        while (reader.hasNext()) {
                            val item = Parser(passedLexer = reader.lexer, streaming = true).parse(reader) as JsonObject
                            onItem(item)
                        }
                    }
                }
            }
        }
    }
}

fun main(args: Array<String>) {

    // "input" simulates the server response 
    val input = ByteArrayInputStream(testJson.encodeToByteArray())

    InputStreamReader(input).use {
        parse(it,
            onResult = { println("""Result: $it""") },
            onItem = { println(it.asIterable().joinToString(", ")) }
        )
    }
}

最好将 Klaxon 与 Kotlin Flow 或 Sequence 集成,但我发现这很困难,因为 beginObject 和 beginArray 包装器不能很好地与挂起函数配合使用。

于 2020-10-16T18:02:44.533 回答