Rust下无需枚举全量变体的动态键JSON API响应建模方案
实现方案
你遇到的是JSON顶层键为动态值的场景,不需要枚举所有股票代码作为枚举变体,serde原生支持将任意键的JSON对象反序列化为HashMap,在此基础上可以灵活实现强类型封装:
基础实现方案
直接用HashMap承载动态股票代码和动态货币的映射关系,不需要调整结构即可兼容新增的股票/货币类型:
use serde::Deserialize; use std::collections::HashMap; // 内层结构:货币代码 -> 价格,货币为动态值时直接用HashMap type CurrencyPrice = HashMap<String, f64>; // 外层结构:股票代码 -> 对应价格结构,股票为动态值时用HashMap承载 #[derive(Debug, Deserialize)] #[serde(transparent)] pub struct PriceResponse(pub HashMap<String, CurrencyPrice>);
反序列化使用示例:
fn main() -> Result<(), Box<dyn std::error::Error>> { let json_resp = r#"{"AAPL": {"usd": 10}, "GOOG": {"usd": 20}, "TSLA": {"usd": 30}}"#; let price_resp: PriceResponse = serde_json::from_str(json_resp)?; // 直接获取指定股票的美元价格 if let Some(aapl_usd) = price_resp.0.get("AAPL").and_then(|p| p.get("usd")) { println!("AAPL 价格:{} USD", aapl_usd); } Ok(()) }
语义化封装优化
可以为结构体添加辅助方法,隐藏内部HashMap实现细节,提升易用性:
impl PriceResponse { /// 获取指定股票、指定货币的价格 pub fn get(&self, stock_code: &str, currency: &str) -> Option<f64> { self.0.get(stock_code)?.get(currency).copied() } /// 遍历所有股票价格 pub fn iter(&self) -> impl Iterator<Item = (&String, &CurrencyPrice)> { self.0.iter() } } // 调用示例 let tsla_price = price_resp.get("TSLA", "usd").unwrap_or_default();
可选优化(金融精度场景)
如果对价格精度要求高,避免浮点数精度误差,可以将价格类型替换为十进制类型:
- 引入依赖:
rust_decimal = { version = "1.30", features = ["serde-float"] } - 将
CurrencyPrice定义改为type CurrencyPrice = HashMap<String, rust_decimal::Decimal>;即可直接适配反序列化,不需要修改其他逻辑。
内容的提问来源于stack exchange,提问作者Corel
相关产品推荐
相关产品推荐

