Rust HJSON处理库deser-hjson的使用:支持人性化JSON格式的序列化与反序列化
Rust HJSON处理库deser-hjson的使用:支持人性化JSON格式的序列化与反序列化
这是一个为HJSON设计的Serde反序列化器,特别适合基于derive的反序列化。
HJSON是一种适合配置文件的优秀语言。这类文件应该由人类编写、阅读和修改,然后由程序反序列化为精确的结构:
let file_content = fs::read_to_string(&file_path)?;
let configuration = deser_hjson::from_str(&file_content);
如果配置文件无效或不符合预期类型,错误会详细说明期望值和错误的确切位置。
示例
use {
deser_hjson::*,
serde::Deserialize,
std::collections::HashMap,
};
// 这个HJSON文档来自HJSON官网
let hjson = r#"
// 使用 #, // 或 /**/ 作为注释
// 键可以省略引号
key: 1
// 字符串可以省略引号
contains: everything on this line
// 行尾可以省略逗号
cool: {
foo: 1
bar: 2
}
// 允许尾随逗号
list: [
1,
2,
]
// 使用多行字符串
realist:
'''
My half empty glass,
I will fill your empty half.
Now you are half full.
'''
"#;
// 我们将把它反序列化为这个结构体:
#[derive(Deserialize, PartialEq, Debug)]
struct Example {
key: i32,
contains: Option<String>,
cool: HashMap<String, u16>,
list: Vec<usize>,
realist: String,
missing: Option<f64>,
}
let mut cool = HashMap::new();
cool.insert("foo".to_owned(), 1);
cool.insert("bar".to_owned(), 2);
let expected = Example {
key: 1,
contains: Some("everything on this line".to_owned()),
cool,
list: vec![1, 2],
realist: "My half empty glass,\nI will fill your empty half.\nNow you are half full.".to_owned(),
missing: None,
};
// 这里是反序列化和相等性检查:
assert_eq!(expected, from_str(hjson).unwrap());
完整示例代码
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use std::fs;
// 定义配置结构体
#[derive(Debug, Serialize, Deserialize)]
struct Config {
server: ServerConfig,
database: DatabaseConfig,
logging: LoggingConfig,
}
#[derive(Debug, Serialize, Deserialize)]
struct ServerConfig {
host: String,
port: u16,
ssl: bool,
}
#[derive(Debug, Serialize, Deserialize)]
struct DatabaseConfig {
url: String,
pool_size: u32,
timeout: f64,
}
#[derive(Debug, Serialize, Deserialize)]
struct LoggingConfig {
level: String,
file: Option<String>,
}
fn main() -> Result<(), Box<dyn std::error::Error>> {
// 示例HJSON配置内容
let hjson_content = r#"
// 服务器配置
server: {
host: localhost
port: 8080
ssl: false
}
// 数据库配置
database: {
url: postgres://user:pass@localhost/db
pool_size: 10
timeout: 30.5
}
// 日志配置
logging: {
level: info
// file是可选的
}
"#;
// 反序列化HJSON到Config结构体
let config: Config = deser_hjson::from_str(hjson_content)?;
println!("{:#?}", config);
// 也可以将结构体序列化为HJSON
let serialized = deser_hjson::to_string_pretty(&config)?;
println!("Serialized HJSON:\n{}", serialized);
Ok(())
}
已知的开源使用案例
- Broot可以使用TOML或HJSON进行配置(选择是基于文件扩展名动态进行的)
- lemmy使用HJSON进行配置
- Resc可以使用JSON或HJSON进行配置
常见问题
它适用于JSON吗?
是的,因为任何JSON文件都可以作为HJSON读取。
为什么只有基于derive的反序列化器?
在具有隐式类型的格式中猜测类型太危险了。当用户输入false
时,它是字符串还是布尔值?当她输入3
时,它是字符串还是数字?虽然不像YAML那样疯狂,但HJSON对此没有内部保护,因此应该只反序列化为显式类型。
为什么只有反序列化器而没有序列化器?
HJSON不是数据交换格式。它旨在由人类编写,充满注释和有意义的格式。虽然在某些上下文中序列化器是有意义的,但它们必须基于模板,或提供其他方法来指定注释和格式,而serde不是适合这种需求的工具。
1 回复
Rust HJSON处理库deser-hjson的使用指南
介绍
deser-hjson是一个Rust库,用于处理HJSON(人性化JSON)格式的数据。HJSON是JSON的一种更易读、更易写的变体,允许注释、省略引号和多行字符串等特性。
主要特性
- 支持标准的JSON解析和序列化
- 支持HJSON扩展语法(注释、无引号键、多行字符串等)
- 与serde框架无缝集成
- 人性化的错误报告
使用方法
添加依赖
在Cargo.toml中添加依赖:
[dependencies]
deser-hjson = "0.1"
serde = { version = "1.0", features = ["derive"] }
基本示例
反序列化HJSON
use serde::Deserialize;
use deser_hjson::from_str;
#[derive(Debug, Deserialize)]
struct Person {
name: String,
age: u8,
hobbies: Vec<String>,
}
fn main() {
let hjson_data = r#"
{
// 这是一个注释
name: John Doe // 键可以不加引号
age: 30
hobbies: [
"reading"
hiking // 字符串也可以不加引号(如果没有特殊字符)
"programming"
]
}"#;
let person: Person = from_str(hjson_data).unwrap();
println!("{:?}", person);
}
序列化为HJSON
use serde::Serialize;
use deser_hjson::to_string_pretty;
#[derive(Debug, Serialize)]
struct Person {
name: String,
age: u8,
hobbies: Vec<String>,
}
fn main() {
let person = Person {
name: "John Doe".to_string(),
age: 30,
hobbies: vec!["reading".to_string(), "hiking".to_string(), "programming".to_string()],
};
let hjson = to_string_pretty(&person).unwrap();
println!("{}", hjson);
}
高级用法
自定义序列化/反序列化
use serde::{Deserialize, Serialize};
use deser_hjson::{from_str, to_string_pretty};
#[derive(Debug, Serialize, Deserialize)]
struct Config {
#[serde(default = "default_host")]
host: String,
port: u16,
#[serde(skip_serializing_if = "Option::is_none")]
timeout: Option<u32>,
}
fn default_host() -> String {
"localhost".to_string()
}
fn main() {
let config_data = r#"
{
// 只指定端口,使用默认主机
port: 8080
}"#;
let config: Config = from_str(config_data).unwrap();
println!("Config: {:?}", config);
let hjson = to_string_pretty(&config).unwrap();
println!("HJSON output:\n{}", hjson);
}
处理多行字符串
use serde::{Deserialize, Serialize};
use deser_hjson::{from_str, to_string_pretty};
#[derive(Debug, Serialize, Deserialize)]
struct Article {
title: String,
content: String, // 多行文本
}
fn main() {
let article_data = r#"
{
title: My First Article
content: '''
这是文章的第一段。
这是第二段,可以包含多行内容。
甚至包含"引号"也不需要转义。
'''
}"#;
let article: Article = from_str(article_data).unwrap();
println!("Article: {:?}", article);
}
完整示例
下面是一个完整的配置管理示例,展示了如何使用deser-hjson读取和写入配置文件:
use serde::{Deserialize, Serialize};
use deser_hjson::{from_str, to_string_pretty};
use std::fs;
#[derive(Debug, Serialize, Deserialize)]
struct AppConfig {
#[serde(default = "default_app_name")]
app_name: String,
debug_mode: bool,
server: ServerConfig,
features: Vec<String>,
}
#[derive(Debug, Serialize, Deserialize)]
struct ServerConfig {
host: String,
port: u16,
#[serde(default = "default_timeout")]
timeout: u32,
}
fn default_app_name() -> String {
"MyApp".to_string()
}
fn default_timeout() -> u32 {
30
}
fn main() {
// 示例HJSON配置文件
let config_data = r#"
{
// 应用配置
debug_mode: true
server: {
host: 127.0.0.1
port: 8080
// 使用默认timeout
}
features: [
logging
auth
"data-processing" // 包含连字符需要引号
]
}"#;
// 解析配置
let config: AppConfig = from_str(config_data).unwrap();
println!("Loaded config: {:#?}", config);
// 修改配置
let mut config = config;
config.server.port = 9090;
config.features.push("caching".to_string());
// 保存配置
let updated_config = to_string_pretty(&config).unwrap();
fs::write("config.hjson", &updated_config).unwrap();
println!("Saved updated config:\n{}", updated_config);
}
注意事项
- HJSON比标准JSON更宽松,但在与标准JSON系统交互时可能需要转换
- 无引号的键和字符串在包含特殊字符时需要加引号
- 注释在序列化时不会被保留
deser-hjson库为需要更人性化配置格式的Rust应用程序提供了很好的支持,特别适合配置文件、文档等场景。