Для строки json, содержащей ...,field=,...
, 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
Я пробовал так много решений, ни одно из них не работает. В том числе:
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)
Что еще я могу сделать?
Проблема не в преобразовании этой строки. Но получить правильную строку. Ответ, который вы говорите «предоставьте данные, подобные этому:», является фактической строкой jsonString, с которой вы должны работать. Вы как-то трансформировали ответ
Ваш jsonString
- это не JSON, а, скорее всего, результат toString, похожий на JSON. Очевидно, вы не можете разобрать его как JSON. Пробовали ли вы просить специалистов по обслуживанию вашего сервера исправить их конечные точки?
Да, оказывается, что указанный API вообще не был действительным JSON. Извините за мое непонимание..
Это недопустимый JSON. Вам нужно разобрать его самостоятельно. Вероятно, эта строка сделана с использованием метода Map::toString()
.
Вот код для его разбора в Map<String, String>
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()
Что делать, если значение поля содержит запятую ,
? Это не сработает? Я думаю, нет необходимости пытаться адаптироваться к сломанному API.
@user12232870 user12232870 Да, это не будет работать должным образом. Мы не знаем, действительно ли ему нужно адаптироваться к сломанному API.
Вы, ребята, правы, данные обратно не являются допустимым JSON, и я не должен адаптироваться к нему. Тем не менее, спасибо @frc129 за попытку исправить строку.
Оказывается, как сказал @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)
Как вы получили эту строку
"{fieldA=stringData,fieldB=,fieldC=}"
? это просто недопустимый JSON. Что вы имеете в виду под "получается читать так:". вы, должно быть, сделали какое-то преобразование к нему