use*_*266 6 rust serde serde-json
使用serde_json,我有String我需要转换为浮点数的JSON对象.我偶然发现了一个自定义解串器解决方案,但它似乎是一个黑客攻击. 这是下面代码的工作游乐场示例.
#[macro_use]
extern crate serde_derive;
extern crate serde;
extern crate serde_json;
use serde_json::Error;
use serde::de::{Deserialize, DeserializeOwned, Deserializer};
#[derive(Serialize, Deserialize)]
struct Example {
#[serde(deserialize_with = "coercible")]
first: f64,
second: f64,
}
fn coercible<'de, T, D>(deserializer: D) -> Result<T, D::Error>
where
T: DeserializeOwned,
D: Deserializer<'de>,
{
use serde::de::Error;
let j = String::deserialize(deserializer)?;
serde_json::from_str(&j).map_err(Error::custom)
}
fn typed_example() -> Result<(), Error> {
let data = r#"["3.141",1.618]"#;
let e: Example = serde_json::from_str(data)?;
println!("{} {}", e.first * 2.0, e.second * 2.0);
Ok(())
}
fn main() {
typed_example().unwrap();
}
Run Code Online (Sandbox Code Playgroud)
上面的代码按照您的预期编译和运行,输出两个浮点数.
我正在努力学习解串器解决方案的工作原理,但我想知道我是朝着正确的方向前进,还是有更好的方法来解决这个问题.
使用coercible工作的意外.有了它,输入"3.141"被剥去了它"",所以我3.141被喂入serde_json::from_str(&j),适当地返回一个浮点数.例如,当输入JSON包含意外值时,这种意外解决方案容易且容易混淆.
我阅读了Serde文档(一个很棒的学习练习)并提出了将字符串转换f64为JSON反序列化的适当方法(这里的工作场所):
#[macro_use]
extern crate serde_derive;
extern crate serde;
extern crate serde_json;
use std::fmt;
use serde_json::Error;
use serde::de::{self, Deserializer, Unexpected, Visitor};
#[derive(Serialize, Deserialize)]
struct Example {
#[serde(deserialize_with = "string_as_f64")]
first: f64,
second: f64,
}
fn string_as_f64<'de, D>(deserializer: D) -> Result<f64, D::Error>
where
D: Deserializer<'de>,
{
deserializer.deserialize_f64(F64Visitor)
}
struct F64Visitor;
impl<'de> Visitor<'de> for F64Visitor {
type Value = f64;
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
formatter.write_str("a string representation of a f64")
}
fn visit_str<E>(self, value: &str) -> Result<f64, E>
where
E: de::Error,
{
value.parse::<f64>().map_err(|_err| {
E::invalid_value(Unexpected::Str(value), &"a string representation of a f64")
})
}
}
fn typed_example() -> Result<(), Error> {
let data = r#"["3.141",1.618]"#;
let e: Example = serde_json::from_str(data)?;
println!("{} {}", e.first * 2.0, e.second * 2.0);
Ok(())
}
fn main() {
typed_example().unwrap();
}
Run Code Online (Sandbox Code Playgroud)
感谢Serde开发者,因为虽然Serde的文档似乎对我的眼睛完全是钝的,但它实际上证明是非常有用和易于理解的.我只需要从顶部开始并慢慢阅读.
轻量级板条箱serde-this-or-that使其成为单行:
use serde_this_or_that::as_f64;
#[derive(Serialize, Deserialize)]
struct Example {
#[serde(deserialize_with = "as_f64")]
first: f64,
second: f64,
}
Run Code Online (Sandbox Code Playgroud)
请注意,serde-this-or-that以可能令人惊讶的方式解决边缘情况。例如,反0.0序列化时将强制转换为空字符串as_f64。
更简洁的自定义(因此可定制)版本(改编自Reddit 评论)将如下所示:
struct Example {
#[serde(deserialize_with = "de_f64_or_string_as_f64")]
first: f64,
second: f64,
}
fn de_f64_or_string_as_f64<'de, D: Deserializer<'de>>(deserializer: D) -> Result<f64, D::Error> {
Ok(match Value::deserialize(deserializer)? {
Value::String(s) => s.parse().map_err(de::Error::custom)?,
Value::Number(num) => num.as_f64().ok_or_else(|| de::Error::custom("Invalid number"))?,
_ => return Err(de::Error::custom("wrong type")),
})
}
Run Code Online (Sandbox Code Playgroud)
当字符串解析失败时返回Nonean可能更安全。Option<f64>诸如此类的上述错误"first": "unparseable"。
以下返回None:
struct Example {
#[serde(default, deserialize_with = "de_f64_or_string_as_f64")]
first: Option<f64>,
second: Option<f64>,
}
fn de_f64_or_string_as_f64<'de, D: Deserializer<'de>>(deserializer: D) -> Result<Option<f64>, D::Error> {
Ok(match Value::deserialize(deserializer)? {
Value::String(s) => s.parse().ok(),
Value::Number(num) => num.as_f64(),
_ => None,
})
}
Run Code Online (Sandbox Code Playgroud)
请注意,我推导default出解析错误会导致None.
| 归档时间: |
|
| 查看次数: |
812 次 |
| 最近记录: |