Moshi解析JSON:如何将空字符串转为数值类型Null(避免重复适配器)
解决Moshi解析空字符串到可空数值类型的通用适配器方案
当使用Moshi解析包含空字符串(如{"appId":"1", "clicks":""})的JSON到带有可空Long/Int等数值字段的Kotlin数据类时,会因类型不匹配抛出Expected a long but was at path $.clicks这类错误。单独为每个数值类型编写适配器会产生大量重复代码,以下是更优的通用解决方案:
通用空字符串转Null数值适配器(基于JsonAdapter.Factory)
通过实现Moshi的JsonAdapter.Factory,可以一次性为所有数值类型(Long、Int、Short、Float、Double等)处理空字符串转null的逻辑,无需重复编写适配器:
import com.squareup.moshi.JsonAdapter import com.squareup.moshi.JsonReader import com.squareup.moshi.JsonWriter import com.squareup.moshi.Moshi import com.squareup.moshi.Types import java.lang.reflect.Type class EmptyStringToNullNumberAdapterFactory : JsonAdapter.Factory { override fun create(type: Type, annotations: MutableSet<out Annotation>, moshi: Moshi): JsonAdapter<*>? { // 仅处理可空的数值类型(Long?、Int?等) val rawType = Types.getRawType(type) if (!Number::class.java.isAssignableFrom(rawType) || !Types.isNullable(type)) { return null } val delegate = moshi.nextAdapter<Number>(this, type, annotations) return object : JsonAdapter<Number?>() { override fun fromJson(reader: JsonReader): Number? { return if (reader.peek() == JsonReader.Token.STRING) { val stringValue = reader.nextString() // 空字符串转null,非空则尝试转换为对应数值类型 if (stringValue.isBlank()) null else convertToNumber(stringValue, rawType) } else { // 非字符串类型交给默认适配器处理 delegate.fromJson(reader) } } override fun toJson(writer: JsonWriter, value: Number?) { // 序列化时直接用默认逻辑 delegate.toJson(writer, value) } private fun convertToNumber(value: String, targetType: Class<*>): Number? { return try { when (targetType) { Long::class.java -> value.toLong() Int::class.java -> value.toInt() Short::class.java -> value.toShort() Float::class.java -> value.toFloat() Double::class.java -> value.toDouble() Byte::class.java -> value.toByte() else -> null } } catch (e: NumberFormatException) { // 转换失败返回null null } } } } }
注册适配器到Moshi实例
创建Moshi实例时,将上述工厂类添加进去即可生效:
val moshi = Moshi.Builder() .add(EmptyStringToNullNumberAdapterFactory()) .build() // 测试解析 val json = """{"appId":"1", "clicks":"", "count":"123"}""" val adjustAdapter = moshi.adapter(Adjust::class.java) val adjust = adjustAdapter.fromJson(json) // 此时adjust.clicks为null,adjust.count为123,符合预期
方案优势
- 一次性覆盖所有数值类型,无需为Long、Int等单独编写适配器
- 自动处理可空字段,非可空数值类型不受影响(若JSON传空字符串到非可空字段,仍会抛出错误,符合预期)
- 支持转换失败时返回null,避免因格式错误的字符串导致解析崩溃
内容的提问来源于stack exchange,提问作者inquizitive
相关产品推荐
相关产品推荐

