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

如何配置JSONEncoder为整数型Double添加小数点及末尾零以生成JSON?

如何让JSONEncoder编码Double类型时保留.0后缀

当然可以实现!默认的JSONEncoder会自动把整数值的Double(比如1.0)优化编码成整数形式(1),但我们可以通过自定义编码逻辑强制保留小数点后缀,让输出符合你的期望。

解决方案:自定义编码逻辑

核心思路是:通过CFNumberCreate明确创建double类型的NSNumber,让JSONEncoder识别这是一个浮点数,即使它的数值是整数,也会输出带小数点的格式。

方法1:针对单个结构体自定义编码

直接在你的NumberTest结构体中实现encode(to:)方法,手动处理number属性的编码:

import Foundation

struct NumberTest: Codable {
    let number: Double
    enum CodingKeys: String, CodingKey {
        case number
    }
    
    func encode(to encoder: Encoder) throws {
        var container = encoder.container(keyedBy: CodingKeys.self)
        // 拷贝Double值到变量(CFNumberCreate需要传入指针)
        var doubleValue = number
        // 创建明确为double类型的CFNumber,再转成NSNumber
        guard let cfNumber = CFNumberCreate(nil, .doubleType, &doubleValue),
              let nsNumber = cfNumber as NSNumber? else {
            throw EncodingError.invalidValue(
                number,
                EncodingError.Context(
                    codingPath: [CodingKeys.number],
                    debugDescription: "Failed to create CFNumber for Double value"
                )
            )
        }
        // 编码这个NSNumber
        try container.encode(nsNumber, forKey: .number)
    }
}

// 测试代码
let doubleNumber = NumberTest(number: 1.0)
if let data = try? JSONEncoder().encode(doubleNumber),
   let jsonString = String(data: data, encoding: .utf8) {
    print(jsonString) // 输出: {"number":1.0}
}

// 测试非整数值的Double也能正常编码
let nonIntegerNumber = NumberTest(number: 1.5)
if let data = try? JSONEncoder().encode(nonIntegerNumber),
   let jsonString = String(data: data, encoding: .utf8) {
    print(jsonString) // 输出: {"number":1.5}
}

方法2:全局扩展Double类型(适用于所有Double属性)

如果你希望项目中所有Double类型的属性都保留小数点后缀,可以给Double添加Codable扩展,统一处理编码逻辑:

import Foundation

extension Double: Codable {
    public func encode(to encoder: Encoder) throws {
        var container = encoder.singleValueContainer()
        var doubleValue = self
        guard let cfNumber = CFNumberCreate(nil, .doubleType, &doubleValue),
              let nsNumber = cfNumber as NSNumber? else {
            throw EncodingError.invalidValue(
                self,
                EncodingError.Context(
                    codingPath: [],
                    debugDescription: "Failed to create CFNumber for Double value"
                )
            )
        }
        try container.encode(nsNumber)
    }
}

// 原结构体不需要修改,直接使用即可
struct NumberTest: Codable {
    let number: Double
    enum CodingKeys: String, CodingKey {
        case number
    }
}

// 测试代码
let doubleNumber = NumberTest(number: 1.0)
if let data = try? JSONEncoder().encode(doubleNumber),
   let jsonString = String(data: data, encoding: .utf8) {
    print(jsonString) // 输出: {"number":1.0}
}

原理说明

默认情况下,当Double值是整数时,JSONEncoder会将其包装为整数类型的NSNumber,序列化时输出整数格式。而通过CFNumberCreate明确指定.doubleType创建的NSNumber,会被JSONEncoder识别为浮点数类型,因此无论数值是否为整数,都会输出带小数点的浮点数格式,完美匹配你的需求。

内容的提问来源于stack exchange,提问作者NeverThoughtIWouldPostHere

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.21 06:44:59