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
无效的解决方案
我尝试了很多解决方案,但都不起作用。其中:
- 设置自定义数据类并将值设置为null
data class DataExample(
val fieldA: String?,
val fieldB: String?,
val fieldC: String?,
)
val parseToObject = Gson().fromJson(jsonString, DataExample::class.java)
- 改为使用
JsonElement
:
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)
或在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)