Kotlin Multi Platform中Double长小数序列化格式异常问题
KMP中Double序列化出现科学计数法格式异常的解决办法
问题描述
在Kotlin Multi Platform(KMP)开发中,使用kotlinx.serialization序列化包含多位小数的Double类型时,出现格式异常:数值如0.0001会被序列化为1.0E-4这种科学计数法格式,而0.001及以上的数值则保持正常的小数格式。
示例输入(Map形式):
mapOf( "test" to listOf( 0.1, 0.01, 0.001, 0.0001, 0.00001, 0.12345, 0.123456789, 0.123467890123, 0.0000012345, ) )
原代码执行后异常输出:
{"test":[0.1,0.01,0.001,1.0E-4,1.0E-5,0.12345,0.123456789,0.123467890123,1.2345E-6]}
原因分析
kotlinx.serialization的默认序列化逻辑在KMP跨平台场景下,会对小于0.001的Double数值自动采用科学计数法,这是为了统一不同平台的Double字符串化行为,但不符合业务对固定小数格式的需求。同时KMP无法使用JVM专属的格式化工具(如DecimalFormat),需要采用跨平台可行的方案。
解决方案
通过自定义Double的格式化逻辑,将其转换为无科学计数法的字符串后再进行序列化,同时保证跨平台兼容性。
修改后的完整代码
import kotlinx.serialization.* import kotlinx.serialization.json.* // 跨平台Double格式化函数,避免科学计数法,清理多余末尾0 fun formatDouble(d: Double): String { return if (d.isNaN() || d.isInfinite()) { d.toString() } else { d.toBigDecimal() .toPlainString() .let { str -> // 去掉末尾的0和多余的小数点 if (str.contains('.')) { str.replace(Regex("0*$"), "").replace(Regex("\\.$"), "") } else { str } } } } fun convertToJsonString(inputMap: Map<String, Any>): String { // 递归处理嵌套结构 fun traverseMap(value: Any): JsonElement { return when (value) { is Map<*, *> -> { JsonObject( value.filter { it.key is String } .map { it as Map.Entry<String, Any> } .associate { it.key to traverseMap(it.value) } ) } is List<*> -> { JsonArray(value.map { traverseMap(it!!) }) } is Double -> { // 对Double单独应用自定义格式化 JsonPrimitive(formatDouble(value)) } is Number, is String, is Boolean -> { JsonPrimitive(value.toString()) } else -> { throw IllegalArgumentException("不支持的数值类型: ${value.javaClass.name}") } } } val processedJson = JsonObject(inputMap.mapValues { traverseMap(it.value) }) return Json.encodeToString(processedJson) } fun main() { val map = mapOf( "test" to listOf( 0.1, 0.01, 0.001, 0.0001, 0.00001, 0.12345, 0.123456789, 0.123467890123, 0.0000012345, ) ) val outputJsonString = convertToJsonString(map) println(outputJsonString) }
关键修改点
- 新增跨平台格式化函数:利用
BigDecimal.toPlainString()将Double转换为无科学计数法的字符串,同时清理末尾多余的0和小数点,保证格式整洁。 - 调整分支顺序:单独处理Double类型,确保自定义逻辑优先执行,避免被通用Number分支覆盖。
- 优化原逻辑:移除重复的Number分支,简化Map过滤代码。
执行结果
修改后输出的JSON所有数值均保持小数格式:
{"test":[0.1,0.01,0.001,0.0001,0.00001,0.12345,0.123456789,0.123467890123,0.0000012345]}
内容的提问来源于stack exchange,提问作者yarlg
相关产品推荐
相关产品推荐

