You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.03 18:20:31