1.什么是JSON? 为什么要使用它? 如何在 Rust 中表示它?
JSON 是一种普遍存在的开放标准格式,它使用人类可读的文本来传输由键值对组成的数据对象。
例如以下 JSON 数据
{
"name": "John Doe",
"age": 43,
"address": {
"street": "10 Downing Street",
"city": "London"
},
"phones": [
"+44 1234567",
"+44 2345678"
]
}
在 Rust 中处理 JSON 数据有三种常见情况。
serde 库使用 ::Value 来表示有效的 json 数据。
enum Value {
Null,
Bool(bool),
Number(Number),
String(String),
Array(Vec
), Object(Map<String, Value>),
}
这个结构体就是::Value,它可以表达任何有效的json数据。
2. 如何将JSON数据转换为Rust值? 如何将 Rust 值转换为 JSON 数据?
(1)使用::函数将json数据字符串转换为Rust值; 使用函数将 JSON 数据字节数组转换为 Rust 值; 使用函数转换从文件或 TCP 流获取的 JSON 数据。
use serde_json::{Result, Value};
fn untyped_example() -> Result<()> {
// Some JSON input data as a &str. Maybe this comes from the user.
let data = r#"
{
"name": "John Doe",
"age": 43,
"phones": [
"+44 1234567",
"+44 2345678"
]
}"#;
// Parse the string of data into serde_json::Value.
let v: Value = serde_json::from_str(data)?;
// Access parts of the data by indexing with square brackets.
println!("Please call {} at the number {}", v["name"], v["phones"][0]);
Ok(())
}
serde::Value 通过方括号括起来的键名的语法来获取值内容。 如果键名不存在或者数组越界,则返回Value::Null,返回值类型为&Value类型。
当打印 Value 值时,字符串将显示双引号。 例如,名称的值为“John Doe”。 如果不想包含双引号,则需要使用()函数将json字符串转换为rust字符串。
在上面的例子中,我们可以发现我们可以直接操作使用serde::Value,对于基本任务来说已经足够了,但是如果你将v["name"]写成v["naem"],编译器无法帮助你找出错误。 我们可以使用 Rust 结构体来解决这个问题。
让我们学习将 json 数据映射到 Rust 结构。
例如:
use serde::{Deserialize, Serialize};
use serde_json::Result;
#[derive(Serialize, Deserialize)]
struct Person {
name: String,
age: u8,
phones: Vec<String>,
}
fn typed_example() -> Result<()> {
// Some JSON input data as a &str. Maybe this comes from the user.
let data = r#"
{
"name": "John Doe",
"age": 43,
"phones": [
"+44 1234567",
"+44 2345678"
]
}"#;
// Parse the string of data into a Person object. This is exactly the
// same function as the one that produced serde_json::Value above, but
// now we are asking it for a Person as output.
let p: Person = serde_json::from_str(data)?;
// Do things just like with any other Rust data structure.
println!("Please call {} at the number {}", p.name, p.phones[0]);
Ok(())
}
和上面的例子一样,也使用了::函数,只不过返回的结果会自动将JSON数据映射到一个结构体中。
任何结构体和枚举都可以使用 #[()] 宏来反序列化 json 字符串并自动将其转换为 Rust 结构体。
现在编译器会检查p.name是否有拼写错误,因为p是一个结构体,IDE也会智能提示p.name字段。
(2) 将Rust值构造为json数据
Value::() 函数将 serde::Value 转换为 json 文本字符串。
a、库提供了json! 直接构造 serde::Value 的宏
例如:
use serde_json::json;
fn main() {
// The type of `john` is `serde_json::Value`
let john = json!({
"name": "John Doe",
"age": 43,
"phones": [
"+44 1234567",
"+44 2345678"
]
});
println!("first phone number: {}", john["phones"][0]);
// Convert to a string of JSON and print it out
println!("{}", john.to_string());
}
b、将Rust结构序列化为json字符串。
例如:
use serde::{Deserialize, Serialize};
use serde_json::Result;
#[derive(Serialize, Deserialize)]
struct Address {
street: String,
city: String,
}
fn print_an_address() -> Result<()> {
// Some data structure.
let address = Address {
street: "10 Downing Street".to_owned(),
city: "London".to_owned(),
};
// Serialize it to a JSON string.
let j = serde_json::to_string(&address)?;
// Print, write to a file, or send to an HTTP server.
println!("{}", j);
Ok(())
}
任何实现特征的东西都可以通过 :: 函数进行序列化。 包含 rust 标准库中的内置函数,例如 Vec 和 ,以及声明 #[()] 的结构体和枚举。
只要有内存分配器,它也可以在没有 Rust 标准库的情况下使用。
通过禁用默认 std 功能并启用 alloc 功能来使用 serde 库。
[dependencies]
serde_json = { version = "1.0", default-features = false, features = ["alloc"] }
以上就完成了常见的JSON数据到Rust值的转换,以及Rust值到JSON数据的转换。 下面描述一些特殊场景。
场景一:
结构体的字段是Name,想将其转换为json键名?
使用serde。
#[derive(Serialize, Deserialize, Debug)]
struct Person {
#[serde(rename = "nickName")]
name: String,
age: u16,
}
场景2:
该结构体的字段是蛇形的,转换为json时使用驼峰式大小写吗?
#[derive(Serialize)]
#[serde(rename_all = "camelCase")]
struct Person {
first_name: String,
last_name: String,
}
场景3:
结构体的某个字段不想被序列化?
#[derive(Serialize)]
struct Resource {
// Always serialized.
name: String,
// Never serialized.
#[serde(skip_serializing)]
hash: String,
// Use a method to decide whether the field should be skipped.
#[serde(skip_serializing_if = "Map::is_empty")]
metadata: Map<String, String>,
}
场景四:
客户端传输的JSON数据中的某个字段是可选的。 如何在 Rust 结构中表示它?
这确实很强大,请看:
use serde::Deserialize;
#[derive(Deserialize, Debug)]
struct Request {
// Use the result of a function as the default if "resource" is
// not included in the input.
#[serde(default = "default_resource")]
resource: String,
// Use the type's implementation of std::default::Default if
// "timeout" is not included in the input.
#[serde(default)]
timeout: Timeout,
// Use a method from the type as the default if "priority" is not
// included in the input. This may also be a trait method.
#[serde(default = "Priority::lowest")]
priority: Priority,
}
fn default_resource() -> String {
"/".to_string()
}
/// Timeout in seconds.
#[derive(Deserialize, Debug)]
struct Timeout(u32);
impl Default for Timeout {
fn default() -> Self {
Timeout(30)
}
}
#[derive(Deserialize, Debug)]
enum Priority { ExtraHigh, High, Normal, Low, ExtraLow }
impl Priority {
fn lowest() -> Self { Priority::ExtraLow }
}
fn main() {
let json = r#"
[
{
"resource": "/users"
},
{
"timeout": 5,
"priority": "High"
}
]
"#;
let requests: Vec<Request> = serde_json::from_str(json).unwrap();
// The first request has resource="/users", timeout=30, priority=ExtraLow
println!("{:?}", requests[0]);
// The second request has resource="/", timeout=5, priority=High
println!("{:?}", requests[1]);
}
场景5:
json中的日期按照指定格式转换?
use chrono::{DateTime, Utc};
use serde::{Serialize, Deserialize};
#[derive(Serialize, Deserialize, Debug)]
pub struct StructWithCustomDate {
// DateTime supports Serde out of the box, but uses RFC3339 format. Provide
// some custom logic to make it use our desired format.
#[serde(with = "my_date_format")]
pub timestamp: DateTime<Utc>,
// Any other fields in the struct.
pub bidder: String,
}
mod my_date_format {
use chrono::{DateTime, Utc, TimeZone};
use serde::{self, Deserialize, Serializer, Deserializer};
const FORMAT: &'static str = "%Y-%m-%d %H:%M:%S";
// The signature of a serialize_with function must follow the pattern:
//
// fn serialize
(&T, S) -> Result// where
// S: Serializer
//
// although it may also be generic over the input types T.
pub fn serialize<S>(
date: &DateTime<Utc>,
serializer: S,
) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
let s = format!("{}", date.format(FORMAT));
serializer.serialize_str(&s)
}
// The signature of a deserialize_with function must follow the pattern:
//
// fn deserialize<'de, D>(D) -> Result
// where
// D: Deserializer<'de>
//
// although it may also be generic over the output types T.
pub fn deserialize<'de, D>(
deserializer: D,
) -> Result<DateTime<Utc>, D::Error>
where
D: Deserializer<'de>,
{
let s = String::deserialize(deserializer)?;
Utc.datetime_from_str(&s, FORMAT).map_err(serde::de::Error::custom)
}
}