如何在Kotlinx Serialization中无损JSON编码BigDecimal与BigInteger
无损序列化Java BigDecimal和BigInteger到JSON(Kotlinx Serialization)
问题背景
使用Kotlin/JVM 1.8.0 + Kotlinx Serialization 1.4.1时,需要将java.math.BigDecimal和java.math.BigInteger序列化为JSON数字(不能转字符串,需保证跨程序解析兼容性),但默认无对应序列化器,且直接用toDouble()/toLong()会导致精度丢失。
解决方案
通过自定义序列化器,直接操作JSON原语(JsonPrimitive),将BigDecimal/BigInteger的字符串表示作为JSON数字输出,同时反序列化时从JSON数字的字符串形式重建对象,实现无损序列化。
自定义序列化器实现
import java.math.BigDecimal import java.math.BigInteger import kotlinx.serialization.KSerializer import kotlinx.serialization.descriptors.PrimitiveSerialDescriptor import kotlinx.serialization.descriptors.PrimitiveKind import kotlinx.serialization.descriptors.SerialDescriptor import kotlinx.serialization.encoding.Decoder import kotlinx.serialization.encoding.Encoder import kotlinx.serialization.json.JsonDecoder import kotlinx.serialization.json.JsonEncoder import kotlinx.serialization.json.JsonPrimitive // BigDecimal 序列化器 object BigDecimalSerializer : KSerializer<BigDecimal> { override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor("java.math.BigDecimal", PrimitiveKind.DOUBLE) override fun serialize(encoder: Encoder, value: BigDecimal) { (encoder as JsonEncoder).encodeJsonElement(JsonPrimitive(value.toPlainString())) } override fun deserialize(decoder: Decoder): BigDecimal { val element = (decoder as JsonDecoder).decodeJsonElement() return BigDecimal((element as JsonPrimitive).content) } } // BigInteger 序列化器 object BigIntegerSerializer : KSerializer<BigInteger> { override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor("java.math.BigInteger", PrimitiveKind.LONG) override fun serialize(encoder: Encoder, value: BigInteger) { (encoder as JsonEncoder).encodeJsonElement(JsonPrimitive(value.toString())) } override fun deserialize(decoder: Decoder): BigInteger { val element = (decoder as JsonDecoder).decodeJsonElement() return BigInteger((element as JsonPrimitive).content) } } // 简化使用的类型别名 typealias BigDecimalJson = @Serializable(with = BigDecimalSerializer::class) BigDecimal typealias BigIntegerJson = @Serializable(with = BigIntegerSerializer::class) BigInteger
使用示例
import kotlinx.serialization.Serializable import kotlinx.serialization.json.Json @Serializable data class FooNumbers( val decimal: BigDecimalJson, val integer: BigIntegerJson, ) fun main() { val fooDecimal = BigDecimal("0.1234567890123456789012345678901234567890") val fooInteger = BigInteger("9876543210987654321098765432109876543210") val fooNumbers = FooNumbers(fooDecimal, fooInteger) println("原始对象: $fooNumbers") val encodedNumbers = Json.encodeToString(fooNumbers) println("编码后的JSON: $encodedNumbers") val decodedFooNumbers = Json.decodeFromString<FooNumbers>(encodedNumbers) println("解码后的对象: $decodedFooNumbers") require(decodedFooNumbers == fooNumbers) { "序列化/反序列化不一致" } println("断言通过,无损序列化成功!") }
执行结果
原始对象: FooNumbers(decimal=0.1234567890123456789012345678901234567890, integer=9876543210987654321098765432109876543210) 编码后的JSON: {"decimal":0.1234567890123456789012345678901234567890,"integer":9876543210987654321098765432109876543210} 解码后的对象: FooNumbers(decimal=0.1234567890123456789012345678901234567890, integer=9876543210987654321098765432109876543210) 断言通过,无损序列化成功!
原理说明
- 序列化时:将BigDecimal转为无科学计数法的字符串(
toPlainString()避免科学计数法输出),BigInteger直接转字符串,再通过JsonEncoder输出为JSON数字原语,而非带引号的字符串类型。 - 反序列化时:从JSON中读取数字的字符串表示,直接用BigDecimal/BigInteger的构造器解析,完全保留精度。
- 符合JSON规范:JSON标准允许任意精度的数字字面量,因此这种输出能被其他支持JSON的程序正确解析。
内容的提问来源于stack exchange,提问作者aSemy
相关产品推荐
相关产品推荐

