Основываясь на этом вопросе в Моши https://github.com/square/moshi/issues/843, состоялась хорошая дискуссия о том, почему и как Моши относится к нулям так, как он это делает. Хотя это имеет большой смысл для меня, я, к сожалению, работаю с API, который периодически возвращает нули, и лучшая обратная связь, которую я получил, - то, что мы должны просто игнорировать их. В указанной выше проблеме один из авторов библиотеки упоминает эту фабрику, которая может помочь.
@Retention(RUNTIME)
@Target(CLASS)
annotation class DefaultIfNull
class DefaultIfNullFactory : JsonAdapter.Factory {
override fun create(type: Type, annotations: MutableSet<out Annotation>,
moshi: Moshi): JsonAdapter<*>? {
if (!Types.getRawType(type).isAnnotationPresent(
DefaultIfNull::class.java)) {
return null
}
val delegate = moshi.nextAdapter<Any>(this, type, annotations)
return object : JsonAdapter<Any>() {
override fun fromJson(reader: JsonReader): Any? {
@Suppress("UNCHECKED_CAST")
val blob = reader.readJsonValue() as Map<String, Any?>
val noNulls = blob.filterValues { it != null }
return delegate.fromJsonValue(noNulls)
}
override fun toJson(writer: JsonWriter, value: Any?) {
return delegate.toJson(writer, value)
}
}
}
}
class NullSkipperTest {
@DefaultIfNull
data class ClassWithDefaults(val foo: String, val bar: String? = "defaultBar")
@Test
fun skipNulls() {
//language=JSON
val json = """{"foo": "fooValue", "bar": null}"""
val adapter = Moshi.Builder()
.add(DefaultIfNullFactory())
.add(KotlinJsonAdapterFactory())
.build()
.adapter(ClassWithDefaults::class.java)
val instance = adapter.fromJson(json)!!
check(instance.bar == "defaultBar")
}
}
Это прекрасно работает, но не является "оптимальным" для моего варианта использования из-за двух отдельных проблем: 1. Это требует аннотирования каждого класса. У меня есть около 200 классов, которые я должен аннотировать, и поэтому я ищу фабрику, которая бы просто делала это вместо того, чтобы включить ее. Если что, возможно, я бы хотел отказаться, но это не имеет значения, верно Теперь 2. Из теста, который также был опубликован, не ясно, нужно ли объявлять все мои значения как обнуляемые. Я также хотел бы не отмечать каждое поле как обнуляемый тип, но тест, кажется, проходит как val foo: String, val bar: String = "defaultBar"
вместо val foo: String, val bar: String? = "defaultBar"
Чтобы заставить это работать на себя, я преобразовал фрагмент к этому. И это также, кажется, решает мои две "проблемы", которые я заявляю выше.
class DefaultIfNullFactory : JsonAdapter.Factory {
override fun create(type: Type, annotations: MutableSet<out Annotation>,
moshi: Moshi): JsonAdapter<*>? {
val delegate = moshi.nextAdapter<Any>(this, type, annotations)
return object : JsonAdapter<Any>() {
override fun fromJson(reader: JsonReader): Any? {
val blob1 = reader.readJsonValue()
try {
val blob = blob1 as Map<String, Any?>
val noNulls = blob.filterValues { it != null }
return delegate.fromJsonValue(noNulls)
} catch (e: Exception) {
return delegate.fromJsonValue(blob1)
}
}
override fun toJson(writer: JsonWriter, value: Any?) {
return delegate.toJson(writer, value)
}
}
}
}
То, что я не знал о фабрике, это то, что она перемещается по каждому полю в классе, а также в классе, поэтому, когда у меня изначально было это, я думал, что это будет идеально, но он действительно вызывается для каждого поля внутри каждого класса, не проходит приведение, а затем просто возвращает значение.
Я нахожусь в точке, где я доволен этим, и я не воспринимаю ничего хиты производительности. Кто-нибудь с большим опытом работы с моши скажет, что это ужасно, или это лучшее, что я могу получить для своих ограничений?
РЕДАКТИРОВАТЬ:
Хотя я и не У меня нет ответа на вопрос, является ли это хорошим подходом. Я могу подтвердить, что это породило две скрытые проблемы, и я могу НЕ рекомендовать вам go этот маршрут.
Числа, которые являются длинными в json, такие как 1583674200000
, не будут работать, если вы адаптировали их для преобразования в тип java String. Можно подумать, что вы получите «1583674200000», но на самом деле вы получите «1.5836742E12»
Если у вас есть строковое число, оно будет преобразовано в двойное число. Таким образом, у вас может быть «100», но он будет конвертирован в 100.0.
Это происходит потому, что числа в moshi являются двойными, а причина root - readJsonValue () как Карта, так как она сначала должна отобразить все значения в значения по умолчанию, которые моши может понять, чтобы затем отфильтровать нули. Это причина моих проблем.
TLDR Не используйте эту фабрику.
РЕДАКТИРОВАТЬ 2: Я был уведомлен о том, что мои правки здесь неправильные. Я не совсем уверен, что это так, но мы обсуждаем это здесь https://github.com/square/moshi/issues/843