postgresql 数据库中的数值列序列化为REST端点的文本

pnwntuvh  于 2023-04-20  发布在  PostgreSQL
关注(0)|答案(1)|浏览(101)

我有一个REST端点,它从postgresql数据库中获取一些数据。其中一列是numeric类型的。当我获取数据时,它被序列化为文本,而不是带有小数点的数字。

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

impl Product {
    pub fn find() -> Result<Vec<Product>, CustomError> {
        let q = "
select
  name,
  price
from
  products
order by
  sales_count desc,
  name asc
";

        let mut conn = db::connection()?;
        let p: Vec<Product> = diesel::sql_query(q)
            .get_results(&mut conn)?;
        Ok(p)
    }
}

Cargo.toml:

diesel = { version = "2.0.2", features = ["postgres", "r2d2", "uuid", "chrono", "numeric", "serde_json"] }
serde = { version = "1.0.148", features = ["derive"] }
serde_json = "1.0.89"
bigdecimal = { version = "0.3.0", features = ["serde"] }

$ curl /products | jq
[
  {
    "name": "soda",
    "price": "5.000"
  },
  {
    "name": "ice",
    "price": "4.000"
  }
]

价格用于在客户端计算总和,将价格作为小数而不是文本将避免解析后的转换。我可以更改REST端点以发送数字/小数点的价格吗?

vh0rcniy

vh0rcniy1#

正如@PitaJ所提到的,将BigDecimal s序列化为数字可能不是一个好主意,因为您的客户端不太可能能够处理任意精度。也就是说,这是可能的。
serde_json具有arbitrary_precision特性。不方便的是,这并不能使BigDecimal神奇地工作。在大多数情况下,它只是在内部将serde_json::Number更改为String,并添加一个内部特殊字段名称,这将导致String在JSON中以数字形式发出。现在您可以滥用此功能并自己发出该内部字段名称,但更干净的是,我只需要将BigDecimal转换为serde_json::Number。可悲的是,serde_json::NumberBigDecimal彼此不信任,因此在它们之间转换的唯一方法是通过解析字符串。有了这些知识:

serde_json = { version = "1.0.95", features = ["arbitrary_precision"] }
use bigdecimal::BigDecimal;
use serde::{Deserialize, Serialize};

#[derive(Serialize, Deserialize, Default)]
pub struct Product {
    pub name: String,
    #[serde(serialize_with = "bigdec_as_num")]
    pub price: BigDecimal,
}

fn bigdec_as_num<S: serde::Serializer>(n: &BigDecimal, s: S) -> Result<S::Ok, S::Error> {
    n.to_string()
        .parse::<serde_json::Number>()
        .map_err(serde::ser::Error::custom)
        .and_then(|n| n.serialize(s))
}

fn main() {
    println!(
        "{}",
        serde_json::to_string_pretty(&Product {
            name: "foo".to_string(),
            price: "1000000000000000000.000000000000000000000000000000000001".parse().unwrap(),
        })
        .unwrap()
    );
}

相关问题