Gson - 使用包含 empty/null 值的字段解析 jsonString(在 Kotlin 中)

Gson - parsing jsonString with field containing empty/null value (in Kotlin)

TL;DR

对于包含 ...,field=,... 的 json 字符串,Gson 不断抛出 JsonSyntaxException。我能做什么?

案例

我必须与第三个 api 进行通信,它往往会提供这样的数据:

{
  "fieldA": "stringData",
  "fieldB": "",
  "fieldC": ""
}

然而,在我的应用程序项目中,结果是这样的:

val jsonString = "{fieldA=stringData,fieldB=,fieldC=}"

问题

我尝试使用标准方法反序列化它:

val jsonString = "{fieldA=stringData,fieldB=,fieldC=}"
val parseJson = Gson().fromJson(jsonString, JsonObject::class.java)
assertEquals(3, parseJson.size())

但它导致异常:

com.google.gson.JsonSyntaxException: com.google.gson.stream.MalformedJsonException: Unexpected value at line 1 column 28 path $.fieldB

无效的解决方案

我已经尝试了很多解决方案,其中 none 行得通。其中:

data class DataExample(
    val fieldA: String?,
    val fieldB: String?,
    val fieldC: String?,
)
val parseToObject = Gson().fromJson(jsonString, DataExample::class.java)
data class DataExample(
    val fieldA: JsonElement,
    val fieldB: JsonElement,
    val fieldC: JsonElement,
)
val parseToObject = Gson().fromJson(jsonString, DataExample::class.java)
class EmptyToNullDeserializer<T>: JsonDeserializer<T> {
    override fun deserialize(
        json: JsonElement, typeOfT: Type, context: JsonDeserializationContext
    ): T? {
        if (json.isJsonPrimitive) {
            json.asJsonPrimitive.also {
                if (it.isString && it.asString.isEmpty()) return null
            }
        }
        return context.deserialize(json, typeOfT)
    }
}
data class DataExample(
    @JsonAdapter(EmptyToNullDeserializer::class)
    val fieldA: String?,
    @JsonAdapter(EmptyToNullDeserializer::class)
    val fieldB: String?,
    @JsonAdapter(EmptyToNullDeserializer::class)
    val fieldC: String?,
)
val parseToObject = Gson().fromJson(jsonString, DataExample::class.java)

using it in GsonBuilder:

val gson = GsonBuilder()
    .registerTypeAdapter(DataExample::class.java, EmptyToNullDeserializer<String>())
    .create()
val parseToObject = gson.fromJson(jsonString, DataExample::class.java)

我还能做什么?

这不是有效的 JSON。你需要自己解析。可能这个字符串是使用 Map::toString() 方法制作的。 这是将其解析为 Map

的代码
val jsonString = "{fieldA=stringData,fieldB=,fieldC=}"

val userFieldsMap = jsonString.removeSurrounding("{", "}").split(",") // split by ","
    .mapNotNull { fieldString ->
        val keyVal = fieldString.split("=")
        // check if array contains exactly 2 items
        if (keyVal.size == 2) {
            keyVal[0].trim() to keyVal[1].trim()  // return@mapNotNull
        } else {
            null // return@mapNotNull
        }
    }
    .toMap()

事实证明,正如@frc129 和许多其他人所说,它不是有效的 JSON。

然而事实是,Gson 处理的情况比 JSON 应该处理的多,如下面的数据:

val jsonString = "{fieldA=stringData,fieldB=s2,fieldC=s3}"
val parseJson = Gson().fromJson(jsonString, JsonObject::class.java)
// This will NOT throw exception, even the jsonString here is not actually a JSON string.
assertEquals(3, parseJson.size())
assertEquals("stringData", parseJson["fieldA"].asString)
assertEquals("s2", parseJson["fieldB"].asString)
assertEquals("s3", parseJson["fieldC"].asString)

进一步调查表明——这里和问题中提到的字符串——更像是一个 Map 到字符串。

我对 GSON 处理 Map 有点误解。这应该被视为额外的便利支持,而不是法律程序。总之,不应该转换,数据格式应该是固定的。那我就去处理服务器和基础转换。

在这里留言。如果将来有人想要快速修复字符串,你可以看看@frc129 的答案;然而,理想的解决方案是修复数据提供者以提供“正确的 JSON 格式”:

val jsonString = "{\"fieldA\":\"stringData\",\"fieldB\":\"\",\"fieldC\":\"\"}"
val parseJson = Gson().fromJson(jsonString, JsonObject::class.java)
assertEquals(3, parseJson.size())
assertEquals("stringData", parseJson["fieldA"].asString)
assertEquals("", parseJson["fieldB"].asString)
assertEquals("", parseJson["fieldC"].asString)