Я попытался это сделать и выдвинул две возможные идеи. Я не думаю, что они являются единственным способом достичь этого, но я думаю, что могу поделиться своими мыслями.
Во-первых, я сократил проблему до фактического разбора элементов. Поэтому я удалил модификацию из уравнения и использую следующие jsons:
val json = """{
"addon_item": [{
"subcat_id": "144",
"subcat_name": "EXTRA",
"subcat_name_trans": "",
"multi_option": "multiple",
"multi_option_val": "",
"two_flavor_position": "",
"require_addons": "",
"sub_item": [{
"sub_item_id": "697",
"sub_item_name": "Queso cheddar",
"item_description": "Delicioso queso fundido",
"price": "36331.20",
"price_usd": null
}]
}]
}
""".trimIndent()
(для случая, когда addon_item
является массивом)
val jsonString = """{
"addon_item": "foo"
}
""".trimIndent()
(когда addon_item
является строкой)
Первый подход
Моим первым подходом было моделирование addon_item
как обобщенного c JsonElement
:
data class ItemDetails(
@Expose
@SerializedName("addon_item")
val addonItem: JsonElement? = null
)
(я использую классы данных, потому что Я нахожу их более полезными, но у вас их тоже нет)
Идея здесь в том, чтобы позволить gson
десериализовать его как универсальный c json элемент, и затем вы можете проверить его сами. Поэтому, если мы добавим несколько удобных методов в класс:
data class ItemDetails(
@Expose
@SerializedName("addon_item")
val addonItem: JsonElement? = null
) {
fun isAddOnItemString() =
addonItem?.isJsonPrimitive == true && addonItem.asJsonPrimitive.isString
fun isAddOnItemArray() =
addonItem?.isJsonArray == true
fun addOnItemAsString() =
addonItem?.asString
fun addOnItemAsArray() =
addonItem?.asJsonArray
}
Итак, как вы можете видеть, мы проверяем addOnItem
на предмет того, что он содержит, и в соответствии с этим мы можем получить его содержимое. Вот пример того, как его использовать:
fun main() {
val item = Gson().fromJson(jsonString, ItemDetails::class.java)
println(item.isAddOnItemArray())
println(item.isAddOnItemString())
println(item.addOnItemAsString())
}
Я думаю, что наибольшим преимуществом этого является то, что он довольно прост и вам не требуется настраиваемый лог c для десериализации. Для меня огромным недостатком является потеря безопасности типов.
Вы можете добавить дополнение в виде массива, но это будет массив json элементов, которые необходимо десериализовать «вручную». Следовательно, мой второй подход пытается решить эту проблему.
Второй подход
Идея состоит в том, чтобы использовать запечатанные классы Kotlin и иметь 2 типа дополнений:
sealed class AddOnItems {
data class StringAddOnItems(
val addOn: String
) : AddOnItems()
data class ArrayAddOnItems(
val addOns: List<SubCategory> = emptyList()
) : AddOnItems()
fun isArray() = this is ArrayAddOnItems
fun isString() = this is StringAddOnItems
}
Класс SubCategory
- это то, что было внутри списка. Вот простая версия этого:
data class SubCategory(
@SerializedName("subcat_id")
val id: String
)
Как видите, AddOnItems
- это запечатанный класс, который имеет только 2 возможных типа для вашего варианта использования.
Теперь нам нужен custom deserializer:
class AddOnItemsDeserializer : JsonDeserializer<AddOnItems> {
override fun deserialize(json: JsonElement?, typeOfT: Type?, context: JsonDeserializationContext?) =
when {
json?.isJsonArray == true -> {
AddOnItems.ArrayAddOnItems(context!!.deserialize(
json.asJsonArray,
TypeToken.getParameterized(List::class.java, SubCategory::class.java).type))
}
json?.isJsonPrimitive == true && json.asJsonPrimitive.isString ->
AddOnItems.StringAddOnItems(json.asJsonPrimitive.asString)
else -> throw IllegalStateException("Cannot parse $json as addonItems")
}
}
В двух словах, это проверяет, является ли add on массивом, и создает соответствующий класс и тот же для строки.
Вот как вы можете его использовать:
fun main() {
val item = GsonBuilder()
.registerTypeAdapter(AddOnItems::class.java, AddOnItemsDeserializer())
.create()
.fromJson(jsonString, ItemDetails::class.java)
println(item.addOnItems.isString())
println(item.addOnItemsAsString().addOn)
val item = GsonBuilder()
.registerTypeAdapter(AddOnItems::class.java, AddOnItemsDeserializer())
.create()
.fromJson(json, ItemDetails::class.java)
println(item.addOnItems.isArray())
println(item.addOnItemsAsArray().addOns[0])
}
Я думаю, что самое большое преимущество здесь в том, что вы сохраняете типы. Тем не менее, вам все еще нужно проверить, что это такое, прежде чем звонить addOnItemsAs*
.
Надеюсь, это поможет