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

如何让REST接口返回数据库Numeric类型为数值而非文本?

解决PostgreSQL Numeric字段序列化为JSON数值类型的问题

问题根源是你使用的bigdecimal crate默认将BigDecimal类型序列化为JSON字符串(避免JSON数值的精度丢失问题),但可以通过以下几种方式调整序列化行为,让price字段以数值格式返回:

方法1:使用serde_with实现兼容式数值序列化

这个方案会在保证精度的前提下,尽可能将BigDecimal序列化为JSON数值,无法转换时回退到字符串,适合大多数业务场景。

  1. 首先在Cargo.toml中添加依赖:
serde_with = "3.0.0"
  1. 修改Product结构体的定义:
use serde_with::serde_as;
use bigdecimal::BigDecimal;
use serde::{Serialize, Deserialize};
use diesel::QueryableByName;

#[serde_as]
#[derive(Serialize, Deserialize, QueryableByName)]
pub struct Product {
    #[diesel(sql_type = Text)]
    pub name: String,
    #[diesel(sql_type = Numeric)]
    #[serde_as(as = "serde_with::json::NumberOrString")]
    pub price: BigDecimal,
}

方法2:手动实现序列化逻辑(无额外依赖)

如果不想引入新的 crate,可以自定义序列化函数,将BigDecimal转换为浮点数值:

use serde::{Serialize, Serializer};
use bigdecimal::BigDecimal;
use diesel::QueryableByName;

#[derive(Deserialize, QueryableByName)]
pub struct Product {
    #[diesel(sql_type = Text)]
    pub name: String,
    #[diesel(sql_type = Numeric)]
    pub price: BigDecimal,
}

impl Serialize for Product {
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
    where
        S: Serializer,
    {
        let mut state = serializer.serialize_struct("Product", 2)?;
        state.serialize_field("name", &self.name)?;
        
        // 尝试转换为f64,失败则回退到字符串(根据业务需求调整)
        match self.price.to_f64() {
            Some(num) => state.serialize_field("price", &num)?,
            None => state.serialize_field("price", &self.price.to_string())?,
        }
        
        state.end()
    }
}

方法3:升级bigdecimal版本使用内置特性

如果你升级bigdecimal到0.4.x及以上版本,可以直接利用其内置的serde序列化函数:

  1. 更新Cargo.toml中的依赖:
bigdecimal = { version = "0.4.3", features = ["serde"] }
  1. 修改结构体的serde属性:
use bigdecimal::BigDecimal;
use serde::{Serialize, Deserialize};
use diesel::QueryableByName;

#[derive(Serialize, Deserialize, QueryableByName)]
pub struct Product {
    #[diesel(sql_type = Text)]
    pub name: String,
    #[diesel(sql_type = Numeric)]
    #[serde(serialize_with = "bigdecimal::serde::serialize_as_f64")]
    pub price: BigDecimal,
}

注意事项

  • 如果你的price字段包含超过f64精确表示范围的高精度数值(比如整数部分超过16位),转换为浮点类型会丢失精度,这种情况下建议保留字符串格式,或和客户端约定使用字符串进行计算。
  • 常规的价格场景(如最多2-3位小数),以上方法都能安全返回数值类型,满足客户端直接计算的需求。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.25 08:23:14